// This is a dual threaded test app designed to expose a weakness in // GDI+. The CreateDCA("DISPLAY", NULL, NULL, NULL) used to create our // Globals::DesktopDc during GdiplusStartup has thread affinity (as opposed // to other inputs to CreateDCA) and therefore when the creation thread // is terminated, the global DC goes away. This will cause random drawing // failure in gdiplus. // // The main thread spawns a 'creation' thread to initialize gdiplus and draw // something. When it's done and terminated, the main thread attempts to draw // something on the screen before shutting down gdiplus. By the time the // main thread gets to draw something, the DesktopDc has been cleaned up and // we ASSERT in gdiplus. // // Created: 02/03/2001 [asecchia] // #include "precomp.hpp" using namespace Gdiplus; GdiplusStartupInput sti; ULONG_PTR token; bool gdiplusInitialized = false; DWORD threadId; // This is a CriticalSection Proxy designed to // automatically acquire the critical section // when the instance is created and release // it when it goes out of scope. class ThreadMutex { public: static VOID InitializeCriticalSection() { ::InitializeCriticalSection(&critSec); } static VOID DeleteCriticalSection() { ::DeleteCriticalSection(&critSec); } ThreadMutex() { EnterCriticalSection(&critSec); } ~ThreadMutex() { LeaveCriticalSection(&critSec); } private: static CRITICAL_SECTION critSec; }; CRITICAL_SECTION ThreadMutex::critSec; // This is the main routine for the creation thread. // GDI+ will be initialized on this thread and we'll draw a red rectangle // on the screen. // It's protected under the thread mutex help ensure this thread is done // before the main thread continues. // This is not normally a useful requirement, but for the purposes of this // test, it's important. DWORD WINAPI ThreadProc(VOID*) { ThreadMutex tm; gdiplusInitialized = (Ok == GdiplusStartup(&token, &sti, NULL)); if(gdiplusInitialized) { HDC hdc = GetDC(NULL); // Draw a red rectangle. Graphics g(hdc); SolidBrush brush(Color(0x3fff0000)); g.FillRectangle(&brush, 300, 300, 400, 200); ReleaseDC(NULL, hdc); } return 1; } // Main thread of execution. void __cdecl main( void ) { ThreadMutex::InitializeCriticalSection(); // Make the creation thread. CreateThread( NULL, // LPSECURITY_ATTRIBUTES 0, // same stack size &ThreadProc, 0, // parameter to thread 0, // creation flags &threadId ); // wait for the creation thread to initialize gdiplus. // This ensures the creation thread happens first and ensures the // correct ordering of acquiring the ThreadMutex. do { } while(!gdiplusInitialized); { // block till the ThreadMutex becomes available. // This ensures that the creation thread is done before we get started. ThreadMutex tm; // The thread mutex will ensure that we don't start till the thread // proc for the creation thread is done. However we want to wait till // NTUSER is done cleaning up our thread specific resources during // thread terminationi and that's not protected by the ThreadMutex. // Wait 5 seconds here to ensure that thread termination has enough // time to finish. Sleep(500); // If initialization of gdiplus was successful, draw a blue rectangle. if(gdiplusInitialized) { HDC hdc = GetDC(NULL); // Draw a blue rectangle. Graphics g(hdc); SolidBrush brush(Color(0x3f0000ff)); g.FillRectangle(&brush, 100, 100, 400, 200); ReleaseDC(NULL, hdc); } } // scope barrier so the objects above destruct before we call shutdown. if(gdiplusInitialized) { GdiplusShutdown(token); } ThreadMutex::DeleteCriticalSection(); }