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.

1384 lines
40 KiB

  1. /***************************************************************************\
  2. *
  3. * File: ResourceManager.cpp
  4. *
  5. * Description:
  6. * This file implements the ResourceManager used to setup and maintain all
  7. * Thread, Contexts, and other resources used by and with DirectUser.
  8. *
  9. *
  10. * History:
  11. * 4/18/2000: JStall: Created
  12. *
  13. * Copyright (C) 2000 by Microsoft Corporation. All rights reserved.
  14. *
  15. \***************************************************************************/
  16. #include "stdafx.h"
  17. #include "Services.h"
  18. #include "ResourceManager.h"
  19. #include "Thread.h"
  20. #include "Context.h"
  21. #include "OSAL.h"
  22. #include "Hook.h"
  23. #include <Delayimp.h> // For error handling & advanced features
  24. static const GUID guidCreateBuffer = { 0xd2139559, 0x458b, 0x4ba8, { 0x82, 0x28, 0x34, 0xd7, 0x57, 0x3d, 0xa, 0x8 } }; // {D2139559-458B-4ba8-8228-34D7573D0A08}
  25. static const GUID guidInitGdiplus = { 0x49f9b12e, 0x846b, 0x4973, { 0xab, 0xfb, 0x7b, 0xe3, 0x4b, 0x52, 0x31, 0xfe } }; // {49F9B12E-846B-4973-ABFB-7BE34B5231FE}
  26. /***************************************************************************\
  27. *****************************************************************************
  28. *
  29. * class ResourceManager
  30. *
  31. *****************************************************************************
  32. \***************************************************************************/
  33. #if DBG
  34. static BOOL g_fAlreadyShutdown = FALSE;
  35. #endif // DBG
  36. long ResourceManager::s_fInit = FALSE;
  37. HANDLE ResourceManager::s_hthSRT = NULL;
  38. DWORD ResourceManager::s_dwSRTID = 0;
  39. HANDLE ResourceManager::s_hevReady = NULL;
  40. HGADGET ResourceManager::s_hgadMsg = NULL;
  41. MSGID ResourceManager::s_idCreateBuffer = 0;
  42. MSGID ResourceManager::s_idInitGdiplus = 0;
  43. RMData * ResourceManager::s_pData = NULL;
  44. CritLock ResourceManager::s_lockContext;
  45. CritLock ResourceManager::s_lockComponent;
  46. Thread * ResourceManager::s_pthrSRT = NULL;
  47. GList<Thread>
  48. ResourceManager::s_lstAppThreads;
  49. int ResourceManager::s_cAppThreads = 0;
  50. GList<ComponentFactory>
  51. ResourceManager::s_lstComponents;
  52. BOOL ResourceManager::s_fInitGdiPlus = FALSE;
  53. ULONG_PTR ResourceManager::s_gplToken = 0;
  54. Gdiplus::GdiplusStartupOutput
  55. ResourceManager::s_gpgso;
  56. #if DBG_CHECK_CALLBACKS
  57. int ResourceManager::s_cTotalAppThreads = 0;
  58. BOOL ResourceManager::s_fBadMphInit = FALSE;
  59. #endif
  60. BEGIN_STRUCT(GMSG_CREATEBUFFER, EventMsg)
  61. IN HDC hdc; // DC to be compatible with
  62. IN SIZE sizePxl; // Size of bitmap
  63. OUT HBITMAP hbmpNew; // Allocated bitmap
  64. END_STRUCT(GMSG_CREATEBUFFER)
  65. /***************************************************************************\
  66. *
  67. * ResourceManager::Create
  68. *
  69. * Create() is called when DUser.DLL is loaded to initialize low-level
  70. * services in DirectUser.
  71. *
  72. * NOTE: It is very important to keep this function small and to
  73. * delay-initialize to help keep starting costs low.
  74. *
  75. * NOTE: This function is automatically synchronized because it is called
  76. * in PROCESS_ATTACH in DllMain(). Therefore, only one thread will ever be
  77. * in this function at one time.
  78. *
  79. \***************************************************************************/
  80. HRESULT
  81. ResourceManager::Create()
  82. {
  83. AssertMsg(!g_fAlreadyShutdown, "Ensure shutdown has not already occurred");
  84. #if USE_DYNAMICTLS
  85. g_tlsThread = TlsAlloc();
  86. if (g_tlsThread == (DWORD) -1) {
  87. return E_OUTOFMEMORY;
  88. }
  89. #endif
  90. if (InterlockedCompareExchange(&s_fInit, TRUE, FALSE) == TRUE) {
  91. return S_OK;
  92. }
  93. //
  94. // Initialize low-level resources (such as the heap). This must be
  95. // carefully done since many objects have not been constructed yet.
  96. //
  97. s_hthSRT = NULL;
  98. s_fInit = FALSE;
  99. s_hevReady = NULL;
  100. HRESULT hr = OSAL::Init();
  101. if (FAILED(hr)) {
  102. return hr;
  103. }
  104. //
  105. // Create global services / managers
  106. //
  107. s_pData = ProcessNew(RMData);
  108. if (s_pData == NULL) {
  109. return E_OUTOFMEMORY;
  110. }
  111. return S_OK;
  112. }
  113. /***************************************************************************\
  114. *
  115. * ResourceManager::xwDestroy
  116. *
  117. * xwDestroy() is called when DUser.DLL is unloaded to perform final clean-up
  118. * in DirectUser.
  119. *
  120. * NOTE: This function is automatically synchronized because it is called
  121. * in PROCESS_ATTACH in DllMain(). Therefore, only one thread will ever be
  122. * in this function at one time.
  123. *
  124. \***************************************************************************/
  125. void
  126. ResourceManager::xwDestroy()
  127. {
  128. AssertMsg(!g_fAlreadyShutdown, "Ensure shutdown has not already occurred");
  129. #if DBG
  130. g_fAlreadyShutdown = TRUE;
  131. #endif // DBG
  132. //
  133. // Check if there are any remaining Contexts. Unfortunately, we CAN NOT
  134. // perform any cleanup work since we are in User mode and are limited by
  135. // what we can do while inside the "Loader Lock" in DllMain(). We can not
  136. // clean up any objects because these may cause deadlocks, such as freeing
  137. // another library. We also must be very cautious about waiting on
  138. // anything, since we can easily get into a deadlock.
  139. //
  140. // This is a serious application error. The application MUST call
  141. // ::DeleteHandle() on the Context before the thread exits.
  142. //
  143. if (s_cAppThreads != 0) {
  144. OutputDebugString("ERROR: Not all DirectUser Contexts were destroyed before EndProcess().\n");
  145. PromptInvalid("Not all DirectUser Contexts were destroyed before EndProcess().");
  146. while (!s_lstAppThreads.IsEmpty()) {
  147. Thread * pthr = s_lstAppThreads.UnlinkHead();
  148. pthr->MarkOrphaned();
  149. pthr->GetContext()->MarkOrphaned();
  150. }
  151. s_cAppThreads = 0;
  152. } else {
  153. //
  154. // If there are no leaked application threads, there should no longer be
  155. // any SRT, since it should be cleaned up when the last application
  156. // thread is cleaned up.
  157. //
  158. AssertMsg(s_pthrSRT == NULL, "Destruction should reset s_pthrSRT");
  159. AssertMsg(s_lstAppThreads.IsEmpty(), "Should not have any threads");
  160. }
  161. ForceSetContextHeap(NULL);
  162. #if USE_DYNAMICTLS
  163. Verify(TlsSetValue(g_tlsThread, NULL));
  164. #else
  165. t_pContext = NULL;
  166. t_pThread = NULL;
  167. #endif
  168. //
  169. // Clean up remaining resources.
  170. // NOTE: This can NOT use the Context heaps (via new / delete) because they
  171. // have already been destroyed.
  172. //
  173. ProcessDelete(RMData, s_pData);
  174. s_pData = NULL;
  175. if (s_hevReady != NULL) {
  176. CloseHandle(s_hevReady);
  177. s_hevReady = NULL;
  178. }
  179. //
  180. // Because they are global variables, we need to manually unlink all of the
  181. // Component Factories so that they don't get deleted.
  182. //
  183. s_lstComponents.UnlinkAll();
  184. #if USE_DYNAMICTLS
  185. Verify(TlsFree(g_tlsThread));
  186. g_tlsThread = (DWORD) -1; // TLS slot no longer valid
  187. #endif
  188. }
  189. /***************************************************************************\
  190. *
  191. * ResourceManager::ResetSharedThread
  192. *
  193. * ResetSharedThread() cleans up SRT data.
  194. *
  195. \***************************************************************************/
  196. void
  197. ResourceManager::ResetSharedThread()
  198. {
  199. //
  200. // Access to the SRT is normally serialized through DirectUser's queues.
  201. // In the case where the data is directly being cleaned up, we need to
  202. // guarantee that only one thread is accessing this data. This should
  203. // always be true since it will either be the SRT properly shutting down
  204. // or the main application's thread that is cleaning up dangling Contexts.
  205. //
  206. AssertMsg(s_cAppThreads == 0, "Must not have any outstanding application threads");
  207. s_dwSRTID = 0;
  208. if (s_hgadMsg != NULL) {
  209. ::DeleteHandle(s_hgadMsg);
  210. s_hgadMsg = NULL;
  211. }
  212. s_pthrSRT = NULL;
  213. //
  214. // NOTE: It is important not to call DeleteHandle() on the SRT's Context
  215. // here since this function may be called by the application thread which
  216. // is already cleaning up the Context.
  217. //
  218. }
  219. /***************************************************************************\
  220. *
  221. * ResourceManager::SharedThreadProc
  222. *
  223. * SharedThreadProc() provides a "Shared Resource Thread" that is processes
  224. * requests from other DirectUser threads. The SRT is created in the first
  225. * call to InitContextNL().
  226. *
  227. * NOTE: The SRT is ONLY created when SEPARATE or MULTIPLE threading models
  228. * are used. The SRT is NOT created for SINGLE threading model.
  229. *
  230. \***************************************************************************/
  231. unsigned __stdcall
  232. ResourceManager::SharedThreadProc(
  233. IN void * pvArg)
  234. {
  235. UNREFERENCED_PARAMETER(pvArg);
  236. AssertMsg(s_dwSRTID == 0, "SRT should not already be initialized");
  237. s_dwSRTID = GetCurrentThreadId();
  238. Context * pctx;
  239. INITGADGET ig;
  240. ZeroMemory(&ig, sizeof(ig));
  241. ig.cbSize = sizeof(ig);
  242. ig.nThreadMode = IGTM_SEPARATE;
  243. ig.nMsgMode = IGMM_ADVANCED;
  244. HRESULT hr = InitContextNL(&ig, TRUE /* SRT */, &pctx);
  245. if (FAILED(hr)) {
  246. return hr;
  247. }
  248. //
  249. // Setup a Gadget to receive custom requests to execute on this thread.
  250. // Each of these requests uses a registered message.
  251. //
  252. if (((s_idCreateBuffer = RegisterGadgetMessage(&guidCreateBuffer)) == 0) ||
  253. ((s_idInitGdiplus = RegisterGadgetMessage(&guidInitGdiplus)) == 0) ||
  254. ((s_hgadMsg = CreateGadget(NULL, GC_MESSAGE, SharedEventProc, NULL)) == 0)) {
  255. hr = GetLastError();
  256. goto Exit;
  257. }
  258. //
  259. // The SRT is fully initialized and can start processing messages. Signal
  260. // the calling thread and start the message loop.
  261. //
  262. // NOTE: See MSDN docs for PostThreadMessage() that explain why we need the
  263. // extra PeekMessage() in the beginning to force User to create a queue for
  264. // us.
  265. //
  266. MSG msg;
  267. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  268. Verify(SetEvent(s_hevReady));
  269. BOOL fQuit = FALSE;
  270. while ((!fQuit) && GetMessageEx(&msg, NULL, 0, 0)) {
  271. AssertMsg(IsMultiThreaded(), "Must remain multi-threaded if using SRT");
  272. if (msg.message == WM_QUIT) {
  273. fQuit = TRUE;
  274. }
  275. TranslateMessage(&msg);
  276. DispatchMessage(&msg);
  277. }
  278. //
  279. // Uninitialize GDI+
  280. //
  281. // If GDI+ has been initialized on any thread, we need to uninitialize it
  282. // when the SRT is going away.
  283. //
  284. if (IsInitGdiPlus()) {
  285. (s_gpgso.NotificationUnhook)(s_gplToken);
  286. }
  287. hr = S_OK;
  288. Exit:
  289. //
  290. // The SRT is going away:
  291. // - Clean up remaining SRT data
  292. // - Destroy the SRT's Context
  293. //
  294. ResetSharedThread();
  295. DeleteHandle(pctx->GetHandle());
  296. return hr;
  297. }
  298. /***************************************************************************\
  299. *
  300. * ResourceManager::SharedEventProc
  301. *
  302. * SharedEventProc() processes LPC requests sent to the SRT.
  303. *
  304. * NOTE: The SRT is ONLY created when SEPARATE or MULTIPLE threading models
  305. * are used. The SRT is NOT created for SINGLE threading model.
  306. *
  307. \***************************************************************************/
  308. HRESULT
  309. ResourceManager::SharedEventProc(
  310. IN HGADGET hgadCur,
  311. IN void * pvCur,
  312. IN EventMsg * pMsg)
  313. {
  314. UNREFERENCED_PARAMETER(hgadCur);
  315. UNREFERENCED_PARAMETER(pvCur);
  316. AssertMsg(IsMultiThreaded(), "Must remain multi-threaded if using SRT");
  317. if (pMsg->nMsg == s_idCreateBuffer) {
  318. //
  319. // Create a new bitmap
  320. //
  321. GMSG_CREATEBUFFER * pmsgCB = (GMSG_CREATEBUFFER *) pMsg;
  322. pmsgCB->hbmpNew = CreateCompatibleBitmap(pmsgCB->hdc,
  323. pmsgCB->sizePxl.cx, pmsgCB->sizePxl.cy);
  324. #if DBG
  325. if (pmsgCB->hbmpNew == NULL) {
  326. Trace("CreateCompatibleBitmap failed: LastError = %d\n", GetLastError());
  327. }
  328. #endif // DBG
  329. return DU_S_COMPLETE;
  330. } else if (pMsg->nMsg == s_idInitGdiplus) {
  331. //
  332. // Initialize GDI+
  333. //
  334. (s_gpgso.NotificationHook)(&s_gplToken);
  335. return DU_S_COMPLETE;
  336. }
  337. return DU_S_NOTHANDLED;
  338. }
  339. /***************************************************************************\
  340. *
  341. * ResourceManager::InitContextNL
  342. *
  343. * InitContextNL() initializes a thread into either a new or existing
  344. * DirectUser Context. The Context is valid in the Thread until it is
  345. * explicitely destroyed with ::DeleteHandle() or the thread exits.
  346. *
  347. * NOTE: It is VERY important that the first time this function is called is
  348. * NOT in DllMain() because we need to initialize the SRT. DllMain()
  349. * serializes access across all threads, so we will deadlock. After the first
  350. * Context is successfully created, additional Contexts can be created inside
  351. * DllMain().
  352. *
  353. * <error> DU_E_GENERIC</>
  354. * <error> E_OUTOFMEMORY</>
  355. * <error> E_NOTIMPL</>
  356. * <error> E_INVALIDARG</>
  357. * <error> DU_E_THREADINGALREADYSET</>
  358. *
  359. \***************************************************************************/
  360. HRESULT
  361. ResourceManager::InitContextNL(
  362. IN INITGADGET * pInit, // Context description
  363. IN BOOL fSharedThread, // Context is for the shared thread
  364. OUT Context ** ppctxNew) // New context
  365. {
  366. HRESULT hr = DU_E_GENERIC;
  367. *ppctxNew = NULL;
  368. #if DBG_CHECK_CALLBACKS
  369. BOOL fInitMPH = FALSE;
  370. #endif
  371. //
  372. // Can not initialize inside DllMain()
  373. //
  374. if (OS()->IsInsideLoaderLock()) {
  375. PromptInvalid("Can not initialize DirectUser inside DllMain()");
  376. return E_INVALIDARG;
  377. }
  378. //
  379. // If Context is already initialized, increment the number of times this
  380. // THREAD has been initialized. We need to remember each thread
  381. // individually. Since we only lock individual threads, we don't need to
  382. // worry about synchronization yet.
  383. //
  384. if (IsInitContext()) {
  385. Thread * pthrExist = GetThread();
  386. AssertInstance(pthrExist); // Initialized Context must already initialize Thread
  387. pthrExist->Lock();
  388. *ppctxNew = pthrExist->GetContext();
  389. return S_OK;
  390. }
  391. //
  392. // Before initializing the new Context, ensure that the Shared Resource
  393. // Thread has been created. We want to create the shared thread Context
  394. // before creating this thread's Context so that as soon as we return,
  395. // everything is valid.
  396. //
  397. // If we are initializing the Shared Resource Thread, don't take the lock.
  398. // This is because we are already inside the lock in InitContextNL() on
  399. // another thread waiting for the SRT to initialize.
  400. //
  401. if (fSharedThread) {
  402. AssertMsg(s_pthrSRT == NULL, "Only should initialize a single SRT");
  403. } else {
  404. s_lockContext.Enter();
  405. }
  406. #if DBG
  407. int DEBUG_cOldAppThreads = s_cAppThreads;
  408. #endif
  409. if (pInit->nThreadMode != IGTM_NONE) {
  410. //
  411. // Setup the threading model. By default, we start in multi-threading
  412. // model. We can only change to single threaded if no threads are
  413. // already initialized.
  414. //
  415. BOOL fAlreadyInit = (!s_lstAppThreads.IsEmpty()) && (s_pthrSRT == NULL);
  416. switch (pInit->nThreadMode)
  417. {
  418. case IGTM_SINGLE:
  419. if (fAlreadyInit) {
  420. hr = DU_E_THREADINGALREADYSET;
  421. goto RawErrorExit;
  422. } else {
  423. g_fThreadSafe = FALSE;
  424. }
  425. break;
  426. case IGTM_SEPARATE:
  427. case IGTM_MULTIPLE:
  428. if (!g_fThreadSafe) {
  429. hr = DU_E_THREADINGALREADYSET;
  430. goto RawErrorExit;
  431. }
  432. break;
  433. default:
  434. AssertMsg(0, "Unknown threading model");
  435. hr = E_INVALIDARG;
  436. goto RawErrorExit;
  437. }
  438. }
  439. if (IsMultiThreaded() && (!fSharedThread)) {
  440. hr = InitSharedThread();
  441. if (FAILED(hr)) {
  442. goto RawErrorExit;
  443. }
  444. }
  445. {
  446. DUserHeap * pHeapNew = NULL;
  447. Context * pctxShare = NULL;
  448. Context * pctxNew = NULL;
  449. Context * pctxActual = NULL;
  450. Thread * pthrNew = NULL;
  451. #if ENABLE_MPH
  452. BOOL fDanglingMPH = FALSE;
  453. #endif
  454. //
  455. // If the Context being created is separate, then it can't be shared.
  456. //
  457. if ((pInit->nThreadMode == IGTM_SEPARATE) && (pInit->hctxShare != NULL)) {
  458. PromptInvalid("Can not use IGTM_SEPARATE for shared Contexts");
  459. hr = E_INVALIDARG;
  460. goto RawErrorExit;
  461. }
  462. //
  463. // Initialize low-level resources (such as the heap). If a Context is
  464. // specified to share resources with, use the existing one. If no Context
  465. // is specified, need to create new resources.
  466. //
  467. // NOTE: If this is running on the main thread, the heap will already have
  468. // been created.
  469. //
  470. BOOL fThreadSafe;
  471. switch (pInit->nThreadMode)
  472. {
  473. case IGTM_SINGLE:
  474. case IGTM_SEPARATE:
  475. fThreadSafe = FALSE;
  476. break;
  477. default:
  478. fThreadSafe = TRUE;
  479. }
  480. DUserHeap::EHeap idHeap;
  481. #ifdef _DEBUG
  482. idHeap = DUserHeap::idCrtDbgHeap;
  483. #else // _DEBUG
  484. switch (pInit->nPerfMode)
  485. {
  486. case IGPM_SPEED:
  487. #ifdef _X86_
  488. idHeap = DUserHeap::idRockAllHeap;
  489. #else
  490. idHeap = DUserHeap::idNtHeap;
  491. #endif
  492. break;
  493. case IGPM_BLEND:
  494. if (IsRemoteSession()) {
  495. idHeap = DUserHeap::idProcessHeap;
  496. } else {
  497. #ifdef _X86_
  498. idHeap = DUserHeap::idRockAllHeap;
  499. #else
  500. idHeap = DUserHeap::idNtHeap;
  501. #endif
  502. }
  503. break;
  504. case IGPM_SIZE:
  505. default:
  506. idHeap = DUserHeap::idProcessHeap;
  507. break;
  508. }
  509. #endif // _DEBUG
  510. if (pInit->hctxShare != NULL) {
  511. BaseObject * pObj = BaseObject::ValidateHandle(pInit->hctxShare);
  512. if (pObj != NULL) {
  513. //
  514. // Note: We need to manually enter the Context here- can not use
  515. // a ContextLock object because the thread is not initialized yet.
  516. //
  517. pctxShare = CastContext(pObj);
  518. if (pctxShare == NULL) {
  519. hr = E_INVALIDARG;
  520. goto ErrorExit;
  521. }
  522. BOOL fError = FALSE;
  523. pctxShare->Enter();
  524. if (pctxShare->GetThreadMode() == IGTM_SEPARATE) {
  525. PromptInvalid("Can not share with an IGTM_SEPARATE Context");
  526. hr = E_INVALIDARG;
  527. fError = TRUE;
  528. } else {
  529. pctxShare->Lock();
  530. DUserHeap * pHeapExist = pctxShare->GetHeap();
  531. DUserHeap * pHeapTemp; // Use temp b/c don't destroy if failure
  532. VerifyMsgHR(CreateContextHeap(pHeapExist, fThreadSafe, idHeap, &pHeapTemp), "Always should be able to copy the heap");
  533. VerifyMsg(pHeapTemp == pHeapExist, "Ensure heaps match");
  534. }
  535. pctxShare->Leave();
  536. if (fError) {
  537. Assert(FAILED(hr));
  538. goto ErrorExit;
  539. }
  540. }
  541. } else {
  542. if (FAILED(CreateContextHeap(NULL, fThreadSafe, idHeap, &pHeapNew))) {
  543. hr = E_OUTOFMEMORY;
  544. goto ErrorExit;
  545. }
  546. }
  547. #if ENABLE_MPH
  548. //
  549. // Setup the WindowManagerHooks. We do this BEFORE setting up the
  550. // thread, since the MPH's will always be "uninit" in the Thread's
  551. // destructor. However, until the thread is successfully setup, the
  552. // MPH's are dangling and need to be cleaned up manually.
  553. //
  554. if (pInit->nMsgMode == IGMM_STANDARD) {
  555. if (!InitMPH()) {
  556. hr = DU_E_CANNOTUSESTANDARDMESSAGING;
  557. goto ErrorExit;
  558. }
  559. fDanglingMPH = TRUE;
  560. #if DBG_CHECK_CALLBACKS
  561. s_fBadMphInit = TRUE;
  562. fInitMPH = TRUE;
  563. #endif
  564. }
  565. #endif
  566. //
  567. // Initialize the Thread
  568. //
  569. AssertMsg(!IsInitThread(), "Thread should not already be initialized");
  570. hr = Thread::Build(fSharedThread, &pthrNew);
  571. if (FAILED(hr)) {
  572. goto ErrorExit;
  573. }
  574. if (fSharedThread) {
  575. Assert(s_pthrSRT == NULL);
  576. s_pthrSRT = pthrNew;
  577. } else {
  578. s_lstAppThreads.Add(pthrNew);
  579. s_cAppThreads++;
  580. #if DBG_CHECK_CALLBACKS
  581. s_cTotalAppThreads++;
  582. #endif
  583. }
  584. #if ENABLE_MPH
  585. fDanglingMPH = FALSE;
  586. #endif
  587. //
  588. // Initialize the actual Context.
  589. //
  590. // NOTE: pHeapNew will only be initialized if we are building a new
  591. // Context. If we are linking into an existing Context, we do not
  592. // create a _new_ Context heap.
  593. //
  594. if (pctxShare == NULL) {
  595. AssertMsg(pHeapNew != NULL, "Must create a new heap for a new Context");
  596. hr = Context::Build(pInit, pHeapNew, &pctxNew);
  597. if (FAILED(hr)) {
  598. goto ErrorExit;
  599. }
  600. pctxActual = pctxNew;
  601. } else {
  602. //
  603. // Linking this thread to a shared Context, so just use the existing
  604. // Context. We have already Lock()'d the Context earlier.
  605. //
  606. AssertMsg(pHeapNew == NULL, "Should not create a new heap for existing Context");
  607. pctxShare->AddCurrentThread();
  608. pctxActual = pctxShare;
  609. }
  610. AssertMsg(fSharedThread || ((s_cAppThreads - DEBUG_cOldAppThreads) == 1),
  611. "Should have created a single new app threads on success");
  612. #if DBG_CHECK_CALLBACKS
  613. s_fBadMphInit = FALSE;
  614. #endif
  615. if (!fSharedThread) {
  616. s_lockContext.Leave();
  617. //
  618. // NOTE: Can no longer goto ErrorExit or RawErrorExit for cleanup
  619. // because we have left s_lockContext.
  620. //
  621. }
  622. *ppctxNew = pctxActual;
  623. return S_OK;
  624. ErrorExit:
  625. //
  626. // NOTE: Do NOT destroy pctxNew on failure, since it was already
  627. // attached to the newly created Thread object. When this Thread is
  628. // unlocked (destroyed), it will also destroy the Context.
  629. //
  630. // If we try and unlock the Context here, it will be unlocked twice.
  631. // See Context::Context() for more information.
  632. //
  633. if (pthrNew != NULL) {
  634. xwDoThreadDestroyNL(pthrNew);
  635. pthrNew = NULL;
  636. }
  637. AssertMsg(DEBUG_cOldAppThreads == s_cAppThreads,
  638. "Should have same number of app threads on failure");
  639. #if ENABLE_MPH
  640. if (fDanglingMPH) {
  641. #if DBG_CHECK_CALLBACKS
  642. if (UninitMPH()) {
  643. s_fBadMphInit = FALSE;
  644. }
  645. #else // DBG_CHECK_CALLBACKS
  646. UninitMPH();
  647. #endif // DBG_CHECK_CALLBACKS
  648. }
  649. #endif // ENABLE_MPH
  650. if (pHeapNew != NULL) {
  651. DestroyContextHeap(pHeapNew);
  652. pHeapNew = NULL;
  653. }
  654. }
  655. RawErrorExit:
  656. #if DBG_CHECK_CALLBACKS
  657. if (fInitMPH) {
  658. if (s_fBadMphInit) {
  659. AlwaysPromptInvalid("Unsuccessfully uninitialized MPH on Context creation failure");
  660. }
  661. }
  662. #endif // DBG_CHECK_CALLBACKS
  663. if (!fSharedThread) {
  664. s_lockContext.Leave();
  665. }
  666. AssertMsg(FAILED(hr), "ErrorExit requires a failure code");
  667. return hr;
  668. }
  669. /***************************************************************************\
  670. *
  671. * ResourceManager::InitComponentNL
  672. *
  673. * InitComponentNL() initializes an optional DirectUser component to be shared
  674. * across all Contexts. The component is valid until either it is explicitely
  675. * uninitialized with UninitComponent() or the process ends.
  676. *
  677. * NOTE: InitComponentNL() doesn't actually synchronize on a Context, but needs
  678. * a context to be initialized so that the threading model is determined.
  679. *
  680. * <error> DU_E_GENERIC</>
  681. * <error> DU_E_CANNOTLOADGDIPLUS</>
  682. *
  683. \***************************************************************************/
  684. HRESULT
  685. ResourceManager::InitComponentNL(
  686. IN UINT nOptionalComponent) // Optional component to load
  687. {
  688. //
  689. // NOTE: Initializing and unintializaing Components CAN NOT use the
  690. // s_lockContext since they may destroy Threads. This will call
  691. // xwNotifyThreadDestroyNL() to cleanup the thread's Context and would
  692. // create a deadlock. Therefore, we must be very careful when
  693. // initializing and uninitializing components because Contexts may be
  694. // created and destroyed in the middle of this.
  695. //
  696. HRESULT hr;
  697. AssertMsg(IsInitContext(), "Context must be initialized to determine threading model");
  698. s_lockComponent.Enter();
  699. switch (nOptionalComponent)
  700. {
  701. case IGC_DXTRANSFORM:
  702. hr = s_pData->manDX.Init();
  703. if (FAILED(hr)) {
  704. goto Exit;
  705. }
  706. hr = s_pData->manDX.InitDxTx();
  707. if (FAILED(hr)) {
  708. goto Exit;
  709. }
  710. hr = S_OK;
  711. break;
  712. case IGC_GDIPLUS:
  713. if (s_fInitGdiPlus) {
  714. hr = S_OK; // GDI+ is already loaded
  715. } else {
  716. //
  717. // GDI+ has not already been loaded, so safely load and initialize
  718. // it.
  719. //
  720. hr = DU_E_CANNOTLOADGDIPLUS; // Assume failure unless pass all tests
  721. Gdiplus::GdiplusStartupInput gpgsi(NULL, TRUE);
  722. if (Gdiplus::GdiplusStartup(&s_gplToken, &gpgsi, &s_gpgso) == Gdiplus::Ok) {
  723. s_fInitGdiPlus = TRUE;
  724. RequestInitGdiplus();
  725. hr = S_OK;
  726. }
  727. }
  728. break;
  729. default:
  730. {
  731. hr = E_NOTIMPL;
  732. ComponentFactory * pfac = s_lstComponents.GetHead();
  733. while (pfac != NULL) {
  734. hr = pfac->Init(nOptionalComponent);
  735. if (hr != E_NOTIMPL) {
  736. break;
  737. }
  738. pfac = pfac->GetNext();
  739. }
  740. }
  741. }
  742. Exit:
  743. s_lockComponent.Leave();
  744. return hr;
  745. }
  746. /***************************************************************************\
  747. *
  748. * ResourceManager::UninitComponentNL
  749. *
  750. * UninitComponentNL() frees up resources associated with a previously
  751. * initialized optional component.
  752. *
  753. \***************************************************************************/
  754. HRESULT
  755. ResourceManager::UninitComponentNL(
  756. IN UINT nOptionalComponent) // Optional component to unload
  757. {
  758. //
  759. // NOTE: See warning in InitComponent() about locks and re-entrancy issues.
  760. //
  761. HRESULT hr;
  762. s_lockComponent.Enter();
  763. switch (nOptionalComponent)
  764. {
  765. case IGC_DXTRANSFORM:
  766. s_pData->manDX.UninitDxTx();
  767. s_pData->manDX.Uninit();
  768. hr = S_OK;
  769. break;
  770. case IGC_GDIPLUS:
  771. //
  772. // GDI+ can not be uninitialized by the application. Since various
  773. // DirectUser objects create and cache GDI+ objects, we have to
  774. // postpone uninitializing GDI+ until all of the Contexts have been
  775. // destroyed.
  776. //
  777. hr = S_OK;
  778. break;
  779. default:
  780. {
  781. hr = E_NOTIMPL;
  782. ComponentFactory * pfac = s_lstComponents.GetHead();
  783. while (pfac != NULL) {
  784. hr = pfac->Init(nOptionalComponent);
  785. if (hr != E_NOTIMPL) {
  786. break;
  787. }
  788. pfac = pfac->GetNext();
  789. }
  790. }
  791. }
  792. s_lockComponent.Leave();
  793. return hr;
  794. }
  795. /***************************************************************************\
  796. *
  797. * ResourceManager::UninitAllComponentsNL
  798. *
  799. * UninitAllComponentsNL() uninitializes all dynamically initialized
  800. * components and other global services. This is called when all application
  801. * threads have been destroyed and DirectUser is shutting down.
  802. *
  803. * NOTE: This may or may not happen inside DllMain().
  804. *
  805. \***************************************************************************/
  806. void
  807. ResourceManager::UninitAllComponentsNL()
  808. {
  809. s_lockComponent.Enter();
  810. s_pData->manDX.Uninit();
  811. if (IsInitGdiPlus()) {
  812. if (!IsMultiThreaded()) {
  813. //
  814. // GDI+ has been initialized, but we are running in single
  815. // threaded mode, so we need to uninitialize GDI+ here
  816. // because there is no SRT.
  817. //
  818. (s_gpgso.NotificationUnhook)(s_gplToken);
  819. }
  820. Gdiplus::GdiplusShutdown(s_gplToken);
  821. }
  822. s_lockComponent.Leave();
  823. }
  824. /***************************************************************************\
  825. *
  826. * ResourceManager::RegisterComponentFactory
  827. *
  828. * RegisterComponentFactory() adds a ComponentFactory to the list of
  829. * factories queried when a dynamic component needs to be initialized.
  830. *
  831. \***************************************************************************/
  832. void
  833. ResourceManager::RegisterComponentFactory(
  834. IN ComponentFactory * pfac)
  835. {
  836. s_lstComponents.Add(pfac);
  837. }
  838. /***************************************************************************\
  839. *
  840. * ResourceManager::InitSharedThread
  841. *
  842. * InitSharedThread() ensures that the SRT for the process has been
  843. * initialized. If it has not already been initialized, the SRT will be
  844. * created and initialized. The SRT is valid until the process shuts down.
  845. *
  846. * NOTE: It is VERY important that the SRT is NOT initialized while processing
  847. * DllMain() because it creates a new thread and blocks until the thread is
  848. * ready to process requests. DllMain() serializes access across all threads,
  849. * so we will deadlock.
  850. *
  851. \***************************************************************************/
  852. HRESULT
  853. ResourceManager::InitSharedThread()
  854. {
  855. AssertMsg(IsMultiThreaded(), "Only initialize when multi-threaded");
  856. if (s_hthSRT != NULL) {
  857. return S_OK;
  858. }
  859. //
  860. // TODO: Need to LoadLibrary() to keep the SRT from going away underneath
  861. // us. We also need to FreeLibrary(), but can not do this inside DllMain().
  862. // Also need to modify all exit paths to properly FreeLibrary() after this.
  863. //
  864. // Create a thread to handle these requests. Wait until an event has been
  865. // signaled that the thread is ready to start receiving events. We need to
  866. // do this to ensure that the msgid's have been properly setup.
  867. //
  868. // This function is already called inside the lock, so we don't need to take
  869. // it again.
  870. //
  871. HRESULT hr;
  872. HINSTANCE hinstLoad = NULL;
  873. AssertMsg(s_hthSRT == NULL, "Ensure Thread is not already initialized");
  874. if (s_hevReady == NULL) {
  875. s_hevReady = CreateEvent(NULL, TRUE, FALSE, NULL);
  876. if (s_hevReady == NULL) {
  877. hr = E_OUTOFMEMORY;
  878. goto Exit;
  879. }
  880. }
  881. AssertMsg(WaitForSingleObject(s_hevReady, 0) == WAIT_TIMEOUT, "Event was not Reset() after used last");
  882. //
  883. // Start the Thread. DirectUser uses the CRT, so we use _beginthreadex().
  884. //
  885. hinstLoad = LoadLibrary("DUser.dll");
  886. AssertMsg(hinstLoad == g_hDll, "Must load the same DLL");
  887. unsigned thrdaddr;
  888. s_hthSRT = (HANDLE) _beginthreadex(NULL, 0, SharedThreadProc, NULL, 0, &thrdaddr);
  889. if (s_hthSRT == NULL) {
  890. hr = E_OUTOFMEMORY;
  891. goto Exit;
  892. }
  893. HANDLE rgh[2];
  894. rgh[0] = s_hevReady;
  895. rgh[1] = s_hthSRT;
  896. switch (WaitForMultipleObjects(_countof(rgh), rgh, FALSE, INFINITE))
  897. {
  898. case WAIT_OBJECT_0:
  899. //
  900. // SRT is now properly setup and ready to process requests.
  901. //
  902. hr = S_OK;
  903. break;
  904. case WAIT_OBJECT_0 + 1:
  905. //
  906. // SRT thread was successfully created, but it failed to setup.
  907. //
  908. {
  909. DWORD dwExitCode;
  910. Verify(GetExitCodeThread(s_hthSRT, &dwExitCode));
  911. hr = (HRESULT) dwExitCode;
  912. //
  913. // NOTE: Calling UninitSharedThread() will clean up both the
  914. // dangling thread handle and DLL hinstances.
  915. //
  916. UninitSharedThread(TRUE /* Aborting */);
  917. }
  918. break;
  919. default:
  920. AssertMsg(0, "Unknown return code");
  921. hr = E_FAIL;
  922. }
  923. ResetEvent(s_hevReady); // Clean up the event for the next user / next time
  924. //
  925. // TODO: May need to change to have a message loop in
  926. // MsgWaitForMultipleObjects() so that we can process UI requests while this
  927. // thread is being created. It may actually be important if this thread
  928. // creates objects that may signal other objects in other threads and could
  929. // potentially dead-lock.
  930. //
  931. hinstLoad = NULL;
  932. Exit:
  933. //
  934. // Need to FreeLibrary() on any errors.
  935. //
  936. if (hinstLoad != NULL) {
  937. FreeLibrary(hinstLoad);
  938. }
  939. return hr;
  940. }
  941. /***************************************************************************\
  942. *
  943. * ResourceManager::UninitSharedThread
  944. *
  945. * UninitSharedThread() uninitializes the SRT and is called when all
  946. * application threads have been uninitialized.
  947. *
  948. \***************************************************************************/
  949. void
  950. ResourceManager::UninitSharedThread(
  951. IN BOOL fAbortInit) // Aborting SRT thread initialization
  952. {
  953. AssertMsg(IsMultiThreaded(), "Only initialize when multi-threaded");
  954. //
  955. // When destroying the SRT, we need to wait until the SRT has properly
  956. // cleaned up. Because we are waiting, we need to worry about dead-locks.
  957. // Practically, this means that we can not be inside DllMain(), because
  958. // the Loader Lock will be a problem when the SRT tries to unload any
  959. // dynamically loaded DLL's.
  960. //
  961. // To ensure against this, we check that the caller doesn't call
  962. // DeleteHandle() inside the Loader Lock. We will still allow it (and
  963. // dead-lock the app), but we Prompt and notify the developer that their
  964. // application is busted and needs to properly call DeleteHandle() before
  965. // entering the Loader Lock.
  966. //
  967. AssertMsg(s_dwSRTID != 0, "Must have valid SRT Thread ID");
  968. if (!fAbortInit) {
  969. Verify(PostThreadMessage(s_dwSRTID, WM_QUIT, 0, 0));
  970. WaitForSingleObject(s_hthSRT, INFINITE);
  971. }
  972. FreeLibrary(g_hDll);
  973. CloseHandle(s_hthSRT);
  974. s_hthSRT = NULL;
  975. }
  976. /***************************************************************************\
  977. *
  978. * ResourceManager::xwNotifyThreadDestroyNL
  979. *
  980. * xwNotifyThreadDestroyNL() is called by DllMain when a thread has been
  981. * destroyed. This provides DirectUser an opportunity to clean up resources
  982. * associated with the Thread before all Thread's are cleaned up at the end
  983. * of the application.
  984. *
  985. \***************************************************************************/
  986. void
  987. ResourceManager::xwNotifyThreadDestroyNL()
  988. {
  989. Thread * pthrDestroy = RawGetThread();
  990. if (pthrDestroy != NULL) {
  991. BOOL fValid = pthrDestroy->Unlock();
  992. if (!fValid) {
  993. //
  994. // The Thread has finally been unlocked, so we can start its
  995. // destruction
  996. //
  997. BOOL fSRT = pthrDestroy->IsSRT();
  998. if (!fSRT) {
  999. s_lockContext.Enter();
  1000. }
  1001. xwDoThreadDestroyNL(pthrDestroy);
  1002. if (!fSRT) {
  1003. s_lockContext.Leave();
  1004. }
  1005. }
  1006. }
  1007. }
  1008. /***************************************************************************\
  1009. *
  1010. * ResourceManager::xwDoThreadDestroyNL
  1011. *
  1012. * xwDoThreadDestroyNL() provides the heart of thread destruction. This may
  1013. * be called in several situations:
  1014. * - When DirectUser notices a thread has been destroyed in DllMain()
  1015. * - When the application calls DeleteHandle() on a Context.
  1016. * - When DirectUser is destroying the ResourceManager in DllMain() and is
  1017. * destroying any outstanding threads.
  1018. *
  1019. \***************************************************************************/
  1020. void
  1021. ResourceManager::xwDoThreadDestroyNL(
  1022. IN Thread * pthrDestroy) // Thread to destroy
  1023. {
  1024. //
  1025. // Can not uninitialize inside DllMain(), but we can't just return.
  1026. // Instead, the process is very likely to dead-lock.
  1027. //
  1028. if (OS()->IsInsideLoaderLock()) {
  1029. PromptInvalid("Can not uninitialize DirectUser inside DllMain()");
  1030. }
  1031. BOOL fSRT = pthrDestroy->IsSRT();
  1032. //
  1033. // Destroy the Thread object and reset the t_pThread pointer. As each is
  1034. // extracted, set the current Thread and Context pointers so that the
  1035. // cleanup code can reference these. When finished, set t_pThread and
  1036. // t_pContext to NULL since there is no Thread or Context for this thread.
  1037. //
  1038. #if USE_DYNAMICTLS
  1039. Verify(TlsSetValue(g_tlsThread, pthrDestroy));
  1040. Context * pContext = pthrDestroy->GetContext();
  1041. if (pContext != NULL) {
  1042. ForceSetContextHeap(pContext->GetHeap());
  1043. }
  1044. #else
  1045. t_pThread = pthrDestroy;
  1046. t_pContext = pthrDestroy->GetContext();
  1047. if (t_pContext != NULL) {
  1048. ForceSetContextHeap(t_pContext->GetHeap());
  1049. }
  1050. #endif
  1051. if (fSRT) {
  1052. ResetSharedThread();
  1053. } else {
  1054. s_lstAppThreads.Unlink(pthrDestroy);
  1055. }
  1056. ProcessDelete(Thread, pthrDestroy); // This is the "xw" function
  1057. ForceSetContextHeap(NULL);
  1058. #if USE_DYNAMICTLS
  1059. pContext = NULL;
  1060. Verify(TlsSetValue(g_tlsThread, NULL));
  1061. #else
  1062. t_pContext = NULL;
  1063. t_pThread = NULL;
  1064. #endif
  1065. //
  1066. // Clean-up when there are no longer any application threads:
  1067. // - Destroy the SRT
  1068. // - Destroy global services / managers
  1069. //
  1070. if (!fSRT) {
  1071. if (--s_cAppThreads == 0) {
  1072. if (IsMultiThreaded()) {
  1073. UninitSharedThread(FALSE /* Proper shutdown */);
  1074. } else {
  1075. AssertMsg(s_hthSRT == NULL, "Should never have initialized SRT for single-threaded");
  1076. }
  1077. UninitAllComponentsNL();
  1078. }
  1079. }
  1080. }
  1081. //------------------------------------------------------------------------------
  1082. HBITMAP
  1083. ResourceManager::RequestCreateCompatibleBitmap(
  1084. IN HDC hdc,
  1085. IN int cxPxl,
  1086. IN int cyPxl)
  1087. {
  1088. if (IsMultiThreaded()) {
  1089. GMSG_CREATEBUFFER msg;
  1090. msg.cbSize = sizeof(msg);
  1091. msg.nMsg = s_idCreateBuffer;
  1092. msg.hgadMsg = s_hgadMsg;
  1093. msg.hdc = hdc;
  1094. msg.sizePxl.cx = cxPxl;
  1095. msg.sizePxl.cy = cyPxl;
  1096. msg.hbmpNew = NULL;
  1097. if (DUserSendEvent(&msg, 0) == DU_S_COMPLETE) {
  1098. return msg.hbmpNew;
  1099. }
  1100. OutputDebugString("ERROR: RequestCreateCompatibleBitmap failed\n");
  1101. return NULL;
  1102. } else {
  1103. return CreateCompatibleBitmap(hdc, cxPxl, cyPxl);
  1104. }
  1105. }
  1106. //------------------------------------------------------------------------------
  1107. void
  1108. ResourceManager::RequestInitGdiplus()
  1109. {
  1110. AssertMsg(s_fInitGdiPlus, "Only should call when GDI+ is just initialized");
  1111. if (IsMultiThreaded()) {
  1112. EventMsg msg;
  1113. msg.cbSize = sizeof(msg);
  1114. msg.nMsg = s_idInitGdiplus;
  1115. msg.hgadMsg = s_hgadMsg;
  1116. if (DUserSendEvent(&msg, 0) != DU_S_COMPLETE) {
  1117. OutputDebugString("ERROR: RequestInitGdiplus failed\n");
  1118. }
  1119. } else {
  1120. (s_gpgso.NotificationHook)(&s_gplToken);
  1121. }
  1122. }