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.

953 lines
28 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998-1999 Microsoft Corporation
  4. *
  5. * Abstract:
  6. *
  7. * Display/Palette notification routines for GDI+.
  8. *
  9. * Revision History:
  10. *
  11. * 7/19/99 ericvan
  12. * Created it.
  13. * 9/15/2000 agodfrey
  14. * #175866: Improved GDI+ startup, shutdown and event notification
  15. *
  16. \**************************************************************************/
  17. #include "precomp.hpp"
  18. #include "..\render\vgahash.hpp"
  19. #include <winuser.h>
  20. VOID DisplayNotify();
  21. VOID PaletteNotify();
  22. VOID SysColorNotify();
  23. /////////////////////////////// MESSAGE HANDLERS ///////////////////////////////
  24. /**************************************************************************\
  25. *
  26. * Function Description:
  27. *
  28. * This routine receives a display notification request and appropriately
  29. * readjusts the size and resolution of DCI screen surface.
  30. *
  31. * History:
  32. *
  33. * 7/23/1999 ericvan
  34. * Created it.
  35. *
  36. \**************************************************************************/
  37. VOID DisplayNotify()
  38. {
  39. GpDevice *device = Globals::DesktopDevice;
  40. Devlock devlock(device);
  41. // Check to see if we have switched to a Terminal Server Session
  42. if (GetSystemMetrics(SM_REMOTESESSION))
  43. {
  44. // it is a remote session
  45. Globals::IsTerminalServer = TRUE;
  46. }
  47. else
  48. {
  49. // it isn't a remote session.
  50. Globals::IsTerminalServer = FALSE;
  51. }
  52. Globals::DesktopDriver->DesktopChangeNotification();
  53. DWORD width, height;
  54. width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  55. height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  56. if ((device != NULL) &&
  57. (device->DeviceHdc != NULL) &&
  58. (GetDeviceCaps(device->DeviceHdc, BITSPIXEL) <= 8))
  59. {
  60. // <SystemPalette>
  61. if (device->Palette == NULL)
  62. {
  63. device->Palette = (ColorPalette*)GpMalloc(sizeof(ColorPalette)
  64. + sizeof(ARGB) * 256);
  65. if (device->Palette == NULL)
  66. {
  67. return;
  68. }
  69. }
  70. INT numEntries;
  71. PALETTEENTRY palentry[256];
  72. RGBQUAD rgb[256];
  73. ColorPalette* palette;
  74. palette = device->Palette;
  75. // [agodfrey] On Win9x, GetSystemPaletteEntries(hdc, 0, 256, NULL)
  76. // doesn't do what MSDN says it does. It seems to return the number
  77. // of entries in the logical palette of the DC instead. So we have
  78. // to make it up ourselves.
  79. numEntries = (1 << (GetDeviceCaps(device->DeviceHdc, BITSPIXEL) *
  80. GetDeviceCaps(device->DeviceHdc, PLANES)));
  81. GetSystemPaletteEntries(device->DeviceHdc, 0, 256, &palentry[0]);
  82. palette->Count = numEntries;
  83. for (INT i=0; i<numEntries; i++)
  84. {
  85. palette->Entries[i] = GpColor::MakeARGB(0xFF,
  86. palentry[i].peRed,
  87. palentry[i].peGreen,
  88. palentry[i].peBlue);
  89. rgb[i].rgbRed = palentry[i].peRed;
  90. rgb[i].rgbGreen = palentry[i].peGreen;
  91. rgb[i].rgbBlue = palentry[i].peBlue;
  92. rgb[i].rgbReserved = 0;
  93. }
  94. if (device->DIBSectionBitmap != NULL)
  95. {
  96. SetDIBColorTable(device->DIBSectionHdc, 0, numEntries, &rgb[0]);
  97. }
  98. Globals::PaletteChangeCount++;
  99. }
  100. // Set BufferWidth to 0. This forces ::Start() to recreate the temporary
  101. // BufferDIB at the correct bit depth next time we process any cached records.
  102. // This needs to be done especially if the screen mode is not palettized
  103. // any more since the BufferDIB shouldn't be 8bpp, but reformatted to 32bpp.
  104. device->BufferWidth = 0;
  105. // Recreate the DCI object. If the allocation fails, keep the old one
  106. // so that we don't access violate 'ScanDci' (although we might quite
  107. // happily draw wrong):
  108. EpScanGdiDci *scanDci = new EpScanGdiDci(Globals::DesktopDevice, TRUE);
  109. if (scanDci != NULL)
  110. {
  111. delete Globals::DesktopDevice->ScanDci;
  112. Globals::DesktopDevice->ScanDci = scanDci;
  113. }
  114. // update width and height on desktop surface
  115. // this copies the Device ScanDCI to Screen bitmap.
  116. Globals::DesktopSurface->InitializeForGdiScreen(
  117. Globals::DesktopDevice,
  118. width,
  119. height
  120. );
  121. // Give the driver an opportunity to adjust the surface.
  122. Globals::DesktopDriver->UpdateSurfacePixelFormat(
  123. Globals::DesktopSurface
  124. );
  125. }
  126. /**************************************************************************\
  127. *
  128. * Function Description:
  129. *
  130. * This routine receives a palette change notification request and appropriately
  131. * readjusts the system palette matching.
  132. *
  133. * History:
  134. *
  135. * 7/23/1999 ericvan
  136. * Created it.
  137. *
  138. \**************************************************************************/
  139. VOID PaletteNotify()
  140. {
  141. Devlock devlock(Globals::DesktopDevice);
  142. // update count to force lazy recomputation of translation vector
  143. Globals::PaletteChangeCount++;
  144. // update the system palette
  145. Globals::DesktopDriver->PaletteChangeNotification();
  146. }
  147. /**************************************************************************\
  148. *
  149. * Function Description:
  150. *
  151. * This routine receives a WM_SYSCOLORCHANGE notifications and updates the
  152. * system magic colors.
  153. *
  154. * History:
  155. *
  156. * 1/10/2K ericvan
  157. * Created it.
  158. *
  159. \**************************************************************************/
  160. VOID SysColorNotify()
  161. {
  162. // [ericvan] There is no synchronization here. If a synchronization
  163. // problem should occur, the worst side effect would be a bad
  164. // color which would go away on a repaint. I think we can live with it.
  165. Globals::SystemColors[16] = ::GetSysColor(COLOR_3DSHADOW);
  166. Globals::SystemColors[17] = ::GetSysColor(COLOR_3DFACE);
  167. Globals::SystemColors[18] = ::GetSysColor(COLOR_3DHIGHLIGHT);
  168. Globals::SystemColors[19] = ::GetSysColor(COLOR_DESKTOP);
  169. VGAHashRebuildTable(&Globals::SystemColors[16]);
  170. }
  171. ////////////////////////// MESSAGE/WINEVENT CALLBACKS //////////////////////////
  172. /**************************************************************************\
  173. *
  174. * Function Description:
  175. *
  176. * This routine is the GDI+ hidden window message pump. If the app doesn't
  177. * hook us directly, then we add a top-level window to intercept
  178. * WM_DISPLAYCHANGE and WM_PALETTECHANGED directly.
  179. *
  180. * History:
  181. *
  182. * 7/23/1999 ericvan
  183. * Created it.
  184. *
  185. \**************************************************************************/
  186. LRESULT
  187. CALLBACK
  188. NotificationWndProc(
  189. HWND hwnd,
  190. UINT uMsg,
  191. WPARAM wParam,
  192. LPARAM lParam
  193. )
  194. {
  195. switch (uMsg)
  196. {
  197. case WM_DISPLAYCHANGE:
  198. DisplayNotify();
  199. break;
  200. case WM_PALETTECHANGED:
  201. PaletteNotify();
  202. break;
  203. case WM_SYSCOLORCHANGE:
  204. SysColorNotify();
  205. break;
  206. case WM_WININICHANGE:
  207. if(lParam != 0 &&
  208. lstrcmpiA((LPCSTR)(lParam), "intl") == 0)
  209. {
  210. Globals::UserDigitSubstituteInvalid = TRUE;
  211. } else if ((wParam == SPI_SETFONTSMOOTHING) || (wParam == SPI_SETFONTSMOOTHINGTYPE) ||
  212. (wParam == SPI_SETFONTSMOOTHINGCONTRAST) || (wParam == SPI_SETFONTSMOOTHINGORIENTATION))
  213. {
  214. Globals::CurrentSystemRenderingHintInvalid = TRUE;
  215. }
  216. break;
  217. default:
  218. if (Globals::g_nAccessibilityMessage == uMsg && uMsg >= WM_USER)
  219. {
  220. Globals::g_fAccessibilityPresent = TRUE;
  221. }
  222. else
  223. {
  224. return DefWindowProcA(hwnd, uMsg, wParam, lParam);
  225. }
  226. }
  227. // return 0 if we processed it.
  228. return 0;
  229. }
  230. /**************************************************************************\
  231. *
  232. * Function Description:
  233. *
  234. * This routine is the GDI+ win-event hook. It watches for full-drag
  235. * messages, to let the DCI renderer know when full-drag is being done.
  236. *
  237. * History:
  238. *
  239. * 3/21/2000 andrewgo
  240. * Created it.
  241. *
  242. \**************************************************************************/
  243. VOID
  244. CALLBACK
  245. WinEventProcedure(
  246. HWINEVENTHOOK hWinEventHook,
  247. DWORD event,
  248. HWND hwnd,
  249. LONG idObject,
  250. LONG idChild,
  251. DWORD idEventThread,
  252. DWORD dwmsEventTime
  253. )
  254. {
  255. ASSERT((event == EVENT_SYSTEM_MOVESIZESTART) ||
  256. (event == EVENT_SYSTEM_MOVESIZEEND));
  257. Globals::IsMoveSizeActive = (event == EVENT_SYSTEM_MOVESIZESTART);
  258. }
  259. /////////////////////// MESSAGE/WINEVENT INITIALIZATION ////////////////////////
  260. VOID InternalNotificationShutdown();
  261. /**************************************************************************\
  262. *
  263. * Function Description:
  264. *
  265. * Called by NotificationStartup and BackgroundThreadProc.
  266. * Initializes the hidden window and WinEvent hook.
  267. *
  268. * Preconditions:
  269. *
  270. * BackgroundThreadCriticalSection must be held.
  271. *
  272. * History:
  273. *
  274. * 9/15/2000 agodfrey
  275. * Created it.
  276. *
  277. \**************************************************************************/
  278. BOOL
  279. InternalNotificationStartup()
  280. {
  281. // register a window class
  282. // we force ANSI rep using GDI+ for benefit of Win9x
  283. WNDCLASSA wndClass =
  284. {
  285. 0,
  286. &NotificationWndProc,
  287. 0,
  288. 0,
  289. DllInstance,
  290. NULL,
  291. NULL,
  292. NULL,
  293. "GDI+ Hook Window",
  294. "GDI+ Hook Window Class",
  295. };
  296. Globals::WindowClassAtom = RegisterClassA(&wndClass);
  297. if (!Globals::WindowClassAtom)
  298. {
  299. WARNING(("RegisterClass failed"));
  300. return FALSE;
  301. }
  302. // If this fails, we continue. It just means we won't work properly
  303. // with accessibility software.
  304. Globals::g_nAccessibilityMessage =
  305. RegisterWindowMessageA("GDI+ Accessibility");
  306. Globals::HwndNotify = CreateWindowA((LPCSTR) Globals::WindowClassAtom,
  307. (LPCSTR) "GDI+ Window",
  308. WS_OVERLAPPED | WS_POPUP | WS_MINIMIZE,
  309. 0,
  310. 0,
  311. 1,
  312. 1, // x,y,width,height
  313. NULL, // hWndParent
  314. NULL, // hMenu
  315. DllInstance,
  316. NULL);
  317. if (!Globals::HwndNotify)
  318. {
  319. WARNING(("CreateWindowA failed, the GDI+ hook window does not exist!"));
  320. InternalNotificationShutdown();
  321. return FALSE;
  322. }
  323. // [ericvan] This is BS, but must be done. We only receive palette
  324. // messages if we have called SelectPalette at least once on our primary DC.
  325. {
  326. struct {
  327. LOGPALETTE logpal;
  328. PALETTEENTRY palEntry[256];
  329. } lp;
  330. const ColorPalette* colorPal = GetDefaultColorPalette(PIXFMT_8BPP_INDEXED);
  331. lp.logpal.palVersion = 0x300;
  332. lp.logpal.palNumEntries = static_cast<WORD>(colorPal->Count);
  333. for (INT i=0; i<lp.logpal.palNumEntries; i++)
  334. {
  335. GpColor color(colorPal->Entries[i]);
  336. lp.logpal.palPalEntry[i].peRed = color.GetRed();
  337. lp.logpal.palPalEntry[i].peGreen = color.GetGreen();
  338. lp.logpal.palPalEntry[i].peBlue = color.GetBlue();
  339. lp.logpal.palPalEntry[i].peFlags = 0;
  340. }
  341. HPALETTE hPal = CreatePalette(&lp.logpal);
  342. HDC hdc = GetDC(Globals::HwndNotify);
  343. SelectPalette(hdc, hPal, FALSE);
  344. ReleaseDC(Globals::HwndNotify, hdc);
  345. DeleteObject(hPal);
  346. }
  347. // [andrewgo] On NT, if a DCI lock is held while a window moves, NT is
  348. // forced to redraw the whole screen. If "Show window contents while
  349. // dragging" (AKA "Full-drag") is enabled (it's on by default),
  350. // then this can result in repeated, excessive repaints
  351. // of the whole screen while somone is dragging a window around.
  352. //
  353. // We work around this by disabling DCI rendering while we notice
  354. // that window moves are happening.
  355. if ((Globals::IsNt) && (Globals::SetWinEventHookFunction))
  356. {
  357. Globals::WinEventHandle =
  358. (Globals::SetWinEventHookFunction)(EVENT_SYSTEM_MOVESIZESTART,
  359. EVENT_SYSTEM_MOVESIZEEND,
  360. NULL,
  361. WinEventProcedure,
  362. 0,
  363. 0,
  364. WINEVENT_OUTOFCONTEXT);
  365. ASSERT(Globals::WinEventHandle != NULL);
  366. if (!Globals::WinEventHandle)
  367. {
  368. InternalNotificationShutdown();
  369. return FALSE;
  370. }
  371. }
  372. return TRUE;
  373. }
  374. /**************************************************************************\
  375. *
  376. * Function Description:
  377. *
  378. * Called by NotificationStartup and BackgroundThreadProc.
  379. * (Also by InternalNotificationStartup, to clean up when there's an
  380. * error.)
  381. *
  382. * Destroys the hidden window and WinEvent hook.
  383. *
  384. * Keep this synchronized with SimulateInternalNotificationShutdown.
  385. *
  386. * Preconditions:
  387. *
  388. * BackgroundThreadSection must be held.
  389. *
  390. * History:
  391. *
  392. * 9/15/2000 agodfrey
  393. * Created it.
  394. *
  395. \**************************************************************************/
  396. VOID
  397. InternalNotificationShutdown()
  398. {
  399. if (Globals::UnhookWinEventFunction && Globals::WinEventHandle)
  400. {
  401. (Globals::UnhookWinEventFunction)(Globals::WinEventHandle);
  402. Globals::WinEventHandle = NULL;
  403. }
  404. if (Globals::HwndNotify)
  405. {
  406. if (Globals::IsNt && (Globals::OsVer.dwMajorVersion == 4))
  407. {
  408. // NT 4.0 has a problem in its DestroyWindow that will
  409. // leave the application in a zombie state.
  410. // Leak the window and rely on process cleanup.
  411. }
  412. else
  413. {
  414. DestroyWindow(Globals::HwndNotify);
  415. }
  416. Globals::HwndNotify = NULL;
  417. }
  418. if (Globals::WindowClassAtom)
  419. {
  420. UnregisterClassA((LPCSTR)Globals::WindowClassAtom, DllInstance);
  421. Globals::WindowClassAtom = NULL;
  422. }
  423. }
  424. /**************************************************************************\
  425. *
  426. * Function Description:
  427. *
  428. * If the thread quits without cleaning up, this fixes our state
  429. * to avoid crashing later.
  430. *
  431. * "Cleans up" what it can - keeps the state consistent, but may leak.
  432. *
  433. * Preconditions:
  434. *
  435. * BackgroundThreadCriticalSection must be held.
  436. *
  437. * History:
  438. *
  439. * 9/16/2000 agodfrey
  440. * Created it.
  441. *
  442. \**************************************************************************/
  443. VOID
  444. SimulateInternalNotificationShutdown()
  445. {
  446. // UnhookWinEvent can't be called from a different thread; so if this
  447. // causes a leak, we can't help it.
  448. Globals::WinEventHandle = NULL;
  449. // DestroyWindow can't be called from a different thread; so if this
  450. // causes a leak, we can't help it.
  451. Globals::HwndNotify = NULL;
  452. // I don't know about UnregisterClass. I'm assuming we can't call it here.
  453. // Anyway, the window may not have been destroyed, and MSDN says that must
  454. // happen first. So, if need be, we'll leak this too.
  455. Globals::WindowClassAtom = NULL;
  456. }
  457. /**************************************************************************\
  458. *
  459. * Function Description:
  460. *
  461. * Starts our top-level window, and sets up the WndProc and WinEventHook.
  462. * This must be called from a GUI thread - it's called from either our
  463. * own background thread, or by the app (via callback pointers returned
  464. * from GdiplusStartup).
  465. *
  466. * History:
  467. *
  468. * 9/15/2000 agodfrey
  469. * Created it.
  470. *
  471. \**************************************************************************/
  472. GpStatus WINAPI
  473. NotificationStartup(
  474. OUT ULONG_PTR *token
  475. )
  476. {
  477. GdiplusStartupCriticalSection critsec;
  478. // Generate the first token, if necessary.
  479. // Also handles wraparound.
  480. if (Globals::NotificationInitToken == 0)
  481. {
  482. Globals::NotificationInitToken = GenerateInitToken();
  483. // Make sure that the token isn't one of the "special" values.
  484. if (Globals::NotificationInitToken <= NotificationModuleTokenMax)
  485. {
  486. Globals::NotificationInitToken = NotificationModuleTokenMax + 1;
  487. }
  488. }
  489. // If there's no hidden window yet, create one.
  490. if (Globals::HiddenWindowOwnerToken == NotificationModuleTokenNobody)
  491. {
  492. // If there's a background thread, then the owner should be set to
  493. // 'NotificationModuleTokenGdiplus'.
  494. ASSERT (Globals::ThreadNotify == NULL);
  495. {
  496. // We take BackgroundThreadCriticalSection because that's a
  497. // precondition for InternalNotificationStartup(). I know that we
  498. // don't actually need to (there's no background thread at this
  499. // point) - but code can change, so this is safer.
  500. BackgroundThreadCriticalSection critsec;
  501. if (!InternalNotificationStartup())
  502. {
  503. return GenericError;
  504. }
  505. }
  506. // Store the token of this calling module - when it calls
  507. // NotificationShutdown, we must destroy the hidden window (and
  508. // start up the background thread, if necessary).
  509. Globals::HiddenWindowOwnerToken = Globals::NotificationInitToken;
  510. }
  511. *token = Globals::NotificationInitToken;
  512. // Increment the token counter for the next module
  513. Globals::NotificationInitToken++;
  514. return Ok;
  515. }
  516. /**************************************************************************\
  517. *
  518. * Function Description:
  519. *
  520. * Shuts down our top-level window, WndProc and WinEventHook.
  521. * This must be called from a GUI thread - it's called from either our
  522. * own background thread, or by the app (via callback pointers returned
  523. * from GdiplusStartup).
  524. *
  525. * History:
  526. *
  527. * 9/15/2000 agodfrey
  528. * Created it.
  529. *
  530. \**************************************************************************/
  531. VOID WINAPI
  532. NotificationShutdown(
  533. ULONG_PTR token
  534. )
  535. {
  536. GdiplusStartupCriticalSection critsec;
  537. // The token they pass us should be the one we gave them, so it shouldn't
  538. // be one of the 'special values'.
  539. if (token <= NotificationModuleTokenMax)
  540. {
  541. RIP(("Invalid token passed to NotificationShutdown"));
  542. // Ignore the call.
  543. return;
  544. }
  545. if (token == Globals::HiddenWindowOwnerToken)
  546. {
  547. // The module that created the hidden window is shutting down.
  548. // There shouldn't be a background thread.
  549. ASSERT (Globals::ThreadNotify == NULL);
  550. {
  551. BackgroundThreadCriticalSection critsec;
  552. InternalNotificationShutdown();
  553. }
  554. Globals::HiddenWindowOwnerToken = NotificationModuleTokenNobody;
  555. // If this is not the final module to shut down, start up the
  556. // background thread
  557. if (Globals::LibraryInitRefCount > 1)
  558. {
  559. if (!BackgroundThreadStartup())
  560. {
  561. // !!! [johnstep] Ack, what can we do now? Another client may
  562. // be happily using GDI+ and now we've lost
  563. // our message notifications.
  564. WARNING(("Could not start background thread"));
  565. }
  566. }
  567. }
  568. }
  569. ////////////////////////////// BACKGROUND THREAD ///////////////////////////////
  570. /**************************************************************************\
  571. *
  572. * Function Description:
  573. *
  574. * Thread proc for our background GUI thread. Sets up a hidden window,
  575. * WndProc and WinEventHook, then starts the message loop.
  576. *
  577. * History:
  578. *
  579. * 7/23/1999 ericvan
  580. * Created it.
  581. * 9/15/2000 agodfrey
  582. * #175866: Improved GDI+ startup, shutdown and event notification
  583. *
  584. \**************************************************************************/
  585. DWORD
  586. WINAPI
  587. BackgroundThreadProc(
  588. VOID*
  589. )
  590. {
  591. BOOL error=FALSE;
  592. HANDLE threadQuitEvent;
  593. {
  594. BackgroundThreadCriticalSection critsec;
  595. // Read threadQuitEvent under the critical section - ensures that
  596. // we don't get the NULL that was there before the main thread
  597. // initialized it. We can assume, though, that it won't change until
  598. // this thread ends.
  599. threadQuitEvent = Globals::ThreadQuitEvent;
  600. if (!InternalNotificationStartup())
  601. {
  602. error = TRUE;
  603. }
  604. }
  605. if (error)
  606. {
  607. return 0;
  608. }
  609. // [agodfrey] We used to have a call to "WaitForInputIdle" here,
  610. // which caused problems. It was motivated by Shell and DDE -
  611. // since calling GetMessage() signals user that "the app is
  612. // ready to receive DDE messages", and we were doing
  613. // it in PROCESS_ATTACH, long before the app was really ready.
  614. //
  615. // Now, we simply disallow initializing GDI+ in PROCESS_ATTACH.
  616. // Process window messages
  617. // We use MsgWaitForMultipleObjects, so that we can catch both messages
  618. // and our "quit" event being signalled.
  619. DWORD dwWake;
  620. MSG msg;
  621. BOOL quit = FALSE;
  622. while (!quit)
  623. {
  624. dwWake = MsgWaitForMultipleObjects(
  625. 1,
  626. &threadQuitEvent,
  627. FALSE,
  628. INFINITE,
  629. QS_ALLINPUT);
  630. if (dwWake == WAIT_OBJECT_0)
  631. {
  632. // Our "quit" event was signaled.
  633. quit = TRUE;
  634. break;
  635. }
  636. else if (dwWake == WAIT_OBJECT_0 + 1)
  637. {
  638. // We received a message
  639. while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
  640. {
  641. if (msg.message == WM_QUIT)
  642. {
  643. quit = TRUE;
  644. break;
  645. }
  646. TranslateMessage(&msg);
  647. DispatchMessageA(&msg);
  648. }
  649. }
  650. else
  651. {
  652. RIP(("Unexpected return value from MsgQaitForMultipleObjects"));
  653. }
  654. }
  655. // Clean up:
  656. {
  657. BackgroundThreadCriticalSection critsec;
  658. InternalNotificationShutdown();
  659. }
  660. return 1;
  661. }
  662. /**************************************************************************\
  663. *
  664. * Function Description:
  665. *
  666. * Starts up the background thread. If the user doesn't ask us to piggyback
  667. * our hidden window onto their main GUI thread, we end up here, to create
  668. * our own.
  669. *
  670. * Preconditions:
  671. *
  672. * GdiplusStartupCriticalSection must be held.
  673. *
  674. * History:
  675. *
  676. * 7/23/1999 ericvan
  677. * Created it.
  678. * 9/15/2000 agodfrey
  679. * #175866: Improved GDI+ startup, shutdown and event notification
  680. *
  681. \**************************************************************************/
  682. BOOL
  683. BackgroundThreadStartup()
  684. {
  685. ASSERT(Globals::HiddenWindowOwnerToken == NotificationModuleTokenNobody);
  686. // [agodfrey] Create an event object. We'll use this to tell the
  687. // background thread to quit.
  688. HANDLE threadQuitEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
  689. if (threadQuitEvent == NULL)
  690. {
  691. WARNING(("CreateEvent failed: %d", GetLastError()));
  692. BackgroundThreadShutdown();
  693. return FALSE;
  694. }
  695. {
  696. // Store threadQuitEvent while holding the correct critsec.
  697. BackgroundThreadCriticalSection critsec;
  698. Globals::ThreadQuitEvent = threadQuitEvent;
  699. }
  700. // Create the background thread.
  701. Globals::ThreadNotify = CreateThread(NULL, // LPSECURITY_ATTRIBUTES
  702. 0, // same stack size
  703. &BackgroundThreadProc,
  704. 0, // parameter to thread
  705. 0, // creation flags
  706. &Globals::ThreadId);
  707. if (Globals::ThreadNotify == NULL)
  708. {
  709. BackgroundThreadShutdown();
  710. return FALSE;
  711. }
  712. // Record the fact that GDI+ has its own hidden window, and so
  713. // NotificationStartup shouldn't create another one.
  714. Globals::HiddenWindowOwnerToken = NotificationModuleTokenGdiplus;
  715. return TRUE;
  716. }
  717. /**************************************************************************\
  718. *
  719. * Function Description:
  720. *
  721. * Shuts down the background thread.
  722. *
  723. * Preconditions:
  724. *
  725. * GdiplusStartupCriticalSection must be held.
  726. * BackgroundThreadCriticalSection must *NOT* be held (we would deadlock).
  727. *
  728. * History:
  729. *
  730. * 7/23/1999 ericvan
  731. * Created it.
  732. * 9/15/2000 agodfrey
  733. * #175866: Improved GDI+ startup, shutdown and event notification.
  734. * Made it more robust by adding an event, and changing the thread's
  735. * message loop so that it quits when the event is signaled.
  736. *
  737. \**************************************************************************/
  738. VOID
  739. BackgroundThreadShutdown()
  740. {
  741. // Stop the background thread
  742. if (Globals::ThreadNotify != NULL)
  743. {
  744. ASSERT(Globals::HiddenWindowOwnerToken == NotificationModuleTokenGdiplus);
  745. // We want to be careful not to hold BackgroundThreadCriticalSection
  746. // while we wait for the thread to terminate, since that could
  747. // cause a deadlock situation (our wait would time out).
  748. HANDLE threadQuitEvent;
  749. {
  750. BackgroundThreadCriticalSection critsec;
  751. threadQuitEvent = Globals::ThreadQuitEvent;
  752. }
  753. ASSERT(threadQuitEvent); // If it's NULL, ThreadNotify should be NULL.
  754. SetEvent(threadQuitEvent);
  755. DWORD ret = WaitForSingleObject(Globals::ThreadNotify, INFINITE);
  756. ASSERT(ret == WAIT_OBJECT_0);
  757. CloseHandle(Globals::ThreadNotify);
  758. Globals::ThreadNotify = NULL;
  759. Globals::ThreadId = 0;
  760. Globals::HiddenWindowOwnerToken = NotificationModuleTokenNobody;
  761. }
  762. {
  763. BackgroundThreadCriticalSection critsec;
  764. // [agodfrey] I discovered that, if InternalGdiplusShutdown is called
  765. // from PROCESS_DETACH, the system will have terminated the thread
  766. // already; WaitForSingleObject returns immediately because the
  767. // thread has already stopped running.
  768. //
  769. // In this case, InternalNotificationShutdown() isn't called, i.e. the
  770. // globals it cleans up are still non-NULL. I deem this "ok" because,
  771. // if we're in PROCESS_DETACH, no-one's going to read those variables
  772. // again.
  773. //
  774. // Still, I don't know if there are other legitimate ways for the
  775. // thread to end without it cleaning up properly. So we call
  776. // SimulateInternalNotificationShutdown() just to be safe - it's not
  777. // very expensive.
  778. SimulateInternalNotificationShutdown();
  779. // Destroy the "quit" event
  780. if (Globals::ThreadQuitEvent)
  781. {
  782. CloseHandle(Globals::ThreadQuitEvent);
  783. Globals::ThreadQuitEvent = NULL;
  784. }
  785. }
  786. }