/**************************************************************************\ * * Copyright (c) 1998-1999 Microsoft Corporation * * Abstract: * * Initialization routines for GDI+. * * Revision History: * * 12/02/1998 andrewgo * Created it. * \**************************************************************************/ #include "precomp.hpp" #if GP_ICECAP>1 #include "icecap.h" #endif // Add this to the Globals namespace. namespace Globals { extern BOOL RuntimeInitialized; }; /**************************************************************************\ * * Function Description: * * Generates a token that an init API can return, used to match the * startup call with the shutdown call. * * Return value: * * A non-zero value. It doesn't really matter what it is - it could be * a simple magic number, but we don't want apps relying on it being a * particular value. * * History: * * 09/15/2000 agodfrey * Created it. * \**************************************************************************/ ULONG_PTR GenerateInitToken() { ULONG_PTR ret = GetTickCount(); if (ret == 0) { ret = 1; } return ret; } /**************************************************************************\ * * Function Description: * * This routine should undo all of the initialization done in * 'InternalGdiplusStartup'. * * Notes: * * Whenever this function, or the functions it calls, frees a pointer or * destroys a resource, it should set the corresponding global to NULL. * This is because it's legal for clients to call GdiplusStartup later. * * In addition, for resources, like DC's and DLL handles, we don't want to * call the "destroy" API if the handle is NULL, since it can waste time - * some API's take their time about recognizing the NULL. * * "delete" doesn't have this problem (the compiler generates a * NULL check for us.) * * Preconditions: * * GdiplusStartupCriticalSection must be held. * * History: * * 12/02/1998 andrewgo * Created it. * 10/03/2000 agodfrey * Changed it to zero out any pointers/handles that it cleans up, * so that InternalGdiplusStartup can safely be called later. * \**************************************************************************/ VOID InternalGdiplusShutdown( VOID ) { if (Globals::ThreadNotify != NULL) { BackgroundThreadShutdown(); } // BackgroundThreadShutdown should NULL this variable itself: ASSERT(Globals::ThreadNotify == NULL); delete Globals::PathLookAside; Globals::PathLookAside = NULL; delete Globals::MatrixLookAside; Globals::MatrixLookAside = NULL; delete Globals::PenLookAside; Globals::PenLookAside = NULL; delete Globals::DesktopDevice; Globals::DesktopDevice = NULL; delete Globals::DeviceList; Globals::DeviceList = NULL; delete Globals::EngineDriver; Globals::EngineDriver = NULL; delete Globals::DesktopDriver; Globals::DesktopDriver = NULL; delete Globals::GdiDriver; Globals::GdiDriver = NULL; delete Globals::D3DDriver; Globals::D3DDriver = NULL; delete Globals::InfoDriver; Globals::InfoDriver = NULL; delete Globals::MetaDriver; Globals::MetaDriver = NULL; delete Globals::DesktopSurface; Globals::DesktopSurface = NULL; if (Globals::DdrawHandle) { FreeLibrary(Globals::DdrawHandle); Globals::DdrawHandle = NULL; } if (Globals::CachedGdiRegion) { DeleteObject(Globals::CachedGdiRegion); Globals::CachedGdiRegion = NULL; } if (Globals::DesktopIc) { DeleteDC(Globals::DesktopIc); Globals::DesktopIc = NULL; } delete Globals::FontCollection; Globals::FontCollection = NULL; delete Globals::FontLinkTable; Globals::FontLinkTable = NULL; if (Globals::SurrogateFontsTable!= NULL && Globals::SurrogateFontsTable!= (GpFontFamily **)-1) { GpFree(Globals::SurrogateFontsTable); } Globals::SurrogateFontsTable = (GpFontFamily **) -1; delete Globals::FontCacheLastRecentlyUsedList; Globals::FontCacheLastRecentlyUsedList = NULL; delete Globals::NationalDigitCache; Globals::NationalDigitCache = NULL; // destroy the Generic objects GpStringFormat::DestroyStaticObjects(); delete [] Globals::FontsDirW; Globals::FontsDirW = NULL; delete [] Globals::FontsDirA; Globals::FontsDirA = NULL; if (Globals::LookAsideBuffer) { GpFree(Globals::LookAsideBuffer); Globals::LookAsideBuffer = NULL; } if (Globals::TextCriticalSectionInitialized) { DeleteCriticalSection(&Globals::TextCriticalSection); Globals::TextCriticalSectionInitialized = FALSE; } if (Globals::DcimanHandle) { FreeLibrary(Globals::DcimanHandle); Globals::DcimanHandle = NULL; } // Uninitialize imaging library CleanupImagingLibrary(); GpTextImager::CleanupTextImager(); if (Globals::UniscribeDllModule) { FreeLibrary(Globals::UniscribeDllModule); Globals::UniscribeDllModule = NULL; } if (Globals::RuntimeInitialized) { GpRuntime::Uninitialize(); Globals::RuntimeInitialized = FALSE; } // We leak Globals::Monitors intentionally, so that it can be used // around GdiplusShutdown as well. It's okay because: // // 1) Unless the user has called GdipMonitorControl (to be removed before // we ship), nothing will be leaked. // 2) GpMonitors defines its own new and delete, which bypass GpMalloc/ // GpFree. So, this won't cause us to hit the memory leak assertion. // delete Globals::Monitors; Globals::Monitors = NULL; LoadLibraryCriticalSection::DeleteCriticalSection(); BackgroundThreadCriticalSection::DeleteCriticalSection(); // Perform memory leak detection. // Must be done after all memory cleanup. GpAssertShutdownNoMemoryLeaks(); VERBOSE(("InternalGdiplusShutdown completed")); // This must be done last. GpMallocTrackingCriticalSection::DeleteCriticalSection(); if (GpRuntime::GpMemHeap) { HeapDestroy(GpRuntime::GpMemHeap); GpRuntime::GpMemHeap = NULL; } } /******************************Public*Routine******************************\ * * Routine Name: * * GetLanguageID * * Routine Description: * * This routines returns the default language ID. Normally, we would call * GetLocaleInfoW to get this information but that API is not available in * kernel mode. Since GetLocaleInfoW gets it from the registry we'll do the * same. * * Arguments: none * * Called by: * * Return Value: * * The default language ID. If the call fails it will just return 0x0409 * for English. * \**************************************************************************/ USHORT GetLanguageID(VOID) { // Language ID is the low word of lcid DWORD lcid = GetSystemDefaultLCID(); USHORT result = USHORT(lcid & 0x0000ffff); #if INITIALIZE_DBG TERSE(("Language ID = 0x%04x", result)); #endif return(result); } /**************************************************************************\ * * Function Description: * * Initialize the version-related data. This may be called several times, * so this must not allocate memory, etc. This is called both by * InternalGdiplusStartup() and by the GpObject class constructor. Since an * object can be global, an object may be created before * InternalGdiplusStartup() is called, which is why the GpObject * constructor may need to call this. * * Only state that is needed by our initialization routines should be * initialized here. * * !!! [agodfrey] I disagree with the above. I don't think it's safe to let * apps call us before they call GdiplusStartup. For one thing, we will * erroneously assert that memory was leaked - but I think we could AV. * * It does make life a little tricky for app developers if * they want global objects that call us in their constructors. * We need to publish sample code for how to do this safely * (e.g. see test\gpinit.inc) * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 7/26/1999 DCurtis * \**************************************************************************/ VOID InitVersionInfo() { if (!Globals::VersionInfoInitialized) { Globals::OsVer.dwOSVersionInfoSize = sizeof(Globals::OsVer); GetVersionExA(&Globals::OsVer); Globals::IsNt = (Globals::OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT); Globals::IsWin95 = ((Globals::OsVer.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (Globals::OsVer.dwMajorVersion == 4) && (Globals::OsVer.dwMinorVersion == 0)); Globals::ACP = GetACP(); Globals::VersionInfoInitialized = TRUE; } } VOID SysColorNotify(); /**************************************************************************\ * * Function Description: * * Initialize globals for the GDI+ engine. * * NOTE: Initialization should not be extremely expensive! * Do NOT put a lot of gratuitous junk into here; consider instead * doing lazy initialization. * * Arguments: * * debugEventFunction - A function the caller can give us that we'll call * to report ASSERTs or WARNINGs. Can be NULL. * * Preconditions: * * GdiplusStartupCriticalSection must be held. * * Return Value: * * FALSE if failure (such as low memory). * * History: * * 12/02/1998 andrewgo * Created it. * \**************************************************************************/ GpStatus InternalGdiplusStartup( const GdiplusStartupInput *input) { // Set up the debug event reporting function, before we use ASSERT or // WARNING. Globals::UserDebugEventProc = input->DebugEventCallback; #if GDIPPRIVATEBUILD #define GDIPCREATEUSERNAMEMESSAGE() "This is a private build from " USERNAME \ "\nBuilt on " __DATE__ " " __TIME__ ::MessageBoxA(NULL, GDIPCREATEUSERNAMEMESSAGE(), "Private Build", MB_OK); #undef GDIPCREATEUSERNAMEMESSAGE #endif // Create the GDI+ heap... ASSERT(!GpRuntime::GpMemHeap); GpRuntime::GpMemHeap = HeapCreate(GPMEMHEAPFLAGS, GPMEMHEAPINITIAL, GPMEMHEAPLIMIT); // If we cannot create the heap, give up! if (!GpRuntime::GpMemHeap) goto ErrorOut; // This must happen first. __try { GpMallocTrackingCriticalSection::InitializeCriticalSection(); BackgroundThreadCriticalSection::InitializeCriticalSection(); LoadLibraryCriticalSection::InitializeCriticalSection(); } __except(EXCEPTION_EXECUTE_HANDLER) { // We couldn't allocate the criticalSection // Return an error goto ErrorOut; } // If Allocation failures are turned on, do some initialization here. GpInitializeAllocFailures(); GpStartInitializeAllocFailureMode(); INT height; INT width; GpDevice *device; Globals::RuntimeInitialized = GpRuntime::Initialize(); if (!Globals::RuntimeInitialized) goto ErrorOut; InitVersionInfo(); Globals::CachedGdiRegion = CreateRectRgn(0, 0, 1, 1); if (!Globals::CachedGdiRegion) goto ErrorOut; // Initialize Stack Back Trace functionality if necessary. Globals::CaptureStackBackTraceFunction = NULL; // This stuff requires NT (ntdll.dll) #if GPMEM_ALLOC_CHK if(Globals::IsNt) { HMODULE module = GetModuleHandleA("ntdll.dll"); Globals::CaptureStackBackTraceFunction = (CAPTURESTACKBACKTRACEFUNCTION) GetProcAddress(module, "RtlCaptureStackBackTrace"); } #endif // Memory allocation subsystem is initialized. It is now safe to use // GpMalloc. // Initialize multi-monitor and window event related function pointers { HMODULE module = GetModuleHandleA("user32.dll"); Globals::GetWindowInfoFunction = (GETWINDOWINFOFUNCTION) GetProcAddress(module, "GetWindowInfo"); Globals::GetAncestorFunction = (GETANCESTORFUNCTION) GetProcAddress(module, "GetAncestor"); Globals::GetMonitorInfoFunction = (GETMONITORINFOFUNCTION) GetProcAddress(module, "GetMonitorInfoA"); Globals::EnumDisplayMonitorsFunction = (ENUMDISPLAYMONITORSFUNCTION) GetProcAddress(module, "EnumDisplayMonitors"); Globals::EnumDisplayDevicesFunction = (ENUMDISPLAYDEVICESFUNCTION) GetProcAddress(module, "EnumDisplayDevicesA"); Globals::SetWinEventHookFunction = (SETWINEVENTHOOKFUNCTION) GetProcAddress(module, "SetWinEventHook"); Globals::UnhookWinEventFunction = (UNHOOKWINEVENTFUNCTION) GetProcAddress(module, "UnhookWinEvent"); } // Create the default desktop device representation. if (GetSystemMetrics(SM_REMOTESESSION)) { // it is a remote session Globals::IsTerminalServer = TRUE; } else { // it isn't a remote session. Globals::IsTerminalServer = FALSE; } // On the NT codebase, the CreateDC(DISPLAY, NULL, NULL, NULL) call has // thread affinity. This means that the desktop DC would go away if the // thread which called GdiplusStartup terminated even if we were still // using it. // On NT we create an Info DC which has process affinity. Rendering onto // an Info DC is not supported but that's ok because we always create // DriverMulti on NT - and therefore always render on a monitor specific // DC instead. // Win9x does not have the thread affinity problem and we'd use an IC // if it weren't for the fact that win95 doesn't have EnumDisplayMonitors // and hence uses DriverGdi instead of DriverMulti - rendering directly // on the DesktopIc // see RAID: // 301407 GDI+ Globals::DesktopDc has thread affinity // 312342 CreateDC("Display", NULL, NULL, NULL) has thread affinity. // and gdiplus/test/multithread for a test app that exposes this problem. if(Globals::IsNt) { Globals::DesktopIc = CreateICA("DISPLAY", NULL, NULL, NULL); } else { Globals::DesktopIc = CreateDCA("DISPLAY", NULL, NULL, NULL); } if (!Globals::DesktopIc) { goto ErrorOut; } Globals::DesktopDpiX = (REAL)::GetDeviceCaps(Globals::DesktopIc, LOGPIXELSX); Globals::DesktopDpiY = (REAL)::GetDeviceCaps(Globals::DesktopIc, LOGPIXELSY); if ((Globals::DesktopDpiX <= 0) || (Globals::DesktopDpiY <= 0)) { WARNING(("GetDeviceCaps failed")); Globals::DesktopDpiX = DEFAULT_RESOLUTION; Globals::DesktopDpiY = DEFAULT_RESOLUTION; } device = Globals::DesktopDevice = new GpDevice(Globals::DesktopIc); if (!CheckValid(Globals::DesktopDevice)) goto ErrorOut; Globals::DeviceList = new GpDeviceList(); if(Globals::DeviceList == NULL) goto ErrorOut; // Create the virtual driver representing all GDI+ Eng drawing: Globals::EngineDriver = new DpDriver(device); if (!CheckValid(Globals::EngineDriver)) goto ErrorOut; // Create the driver for use with the desktop device // NOTE: for now we always use the multimon driver. In the future // we will be able to dynamically redirect desktop drawing // through different drivers as the desktop changes. This will // require that we have a mechanism to safely modify various // GDI+ objects in response to the mode change. // Only use multi-mon driver on multi-mon capable systems if(Globals::GetMonitorInfoFunction != NULL && Globals::EnumDisplayMonitorsFunction != NULL) { Globals::DesktopDriver = new DriverMulti(device); if (!CheckValid(Globals::DesktopDriver)) goto ErrorOut; } else { Globals::DesktopDriver = new DriverGdi(device); if (!CheckValid(Globals::DesktopDriver)) goto ErrorOut; } Globals::GdiDriver = new DriverGdi(device); if (!CheckValid(Globals::GdiDriver)) goto ErrorOut; Globals::D3DDriver = new DriverD3D(device); if (!CheckValid(Globals::D3DDriver)) goto ErrorOut; Globals::InfoDriver = new DriverInfo(device); if (!CheckValid(Globals::InfoDriver)) goto ErrorOut; Globals::MetaDriver = new DriverMeta(device); if (!CheckValid(Globals::MetaDriver)) goto ErrorOut; Globals::DesktopSurface = new DpBitmap(); if (!CheckValid(Globals::DesktopSurface)) goto ErrorOut; // Get the multimon meta-desktop resolution. SM_CX/CYVIRTUALSCREEN // doesn't work on Win95 or NT4, though... width = GetSystemMetrics(SM_CXVIRTUALSCREEN); height = GetSystemMetrics(SM_CYVIRTUALSCREEN); if ((width == 0) || (height == 0)) { width = GetSystemMetrics(SM_CXSCREEN); height = GetSystemMetrics(SM_CYSCREEN); } Globals::DesktopSurface->InitializeForGdiScreen( Globals::DesktopDevice, width, height); // Give the driver an opportunity to adjust the surface. // If we're on multimon, we need to fix up the // pixel format for the DesktopSurface. Globals::DesktopDriver->UpdateSurfacePixelFormat( Globals::DesktopSurface ); // GDI+ v1 DCR 336742 // We are disabling image codecs for v1, so ignore the // input->SuppressExternalCodecs flag and hardwire to TRUE. // Jbronsk if (!InitImagingLibrary(TRUE /* suppressExternalCodecs */)) { // If we couldn't initialize the ImagingLibrary goto ErrorOut; } // Initialize the system colors in fastest search order Globals::SystemColors [0] = RGB(0x00,0x00,0x00); Globals::SystemColors [1] = RGB(0xFF,0xFF,0xFF); Globals::SystemColors [2] = RGB(0xC0,0xC0,0xC0); Globals::SystemColors [3] = RGB(0x80,0x80,0x80); Globals::SystemColors [4] = RGB(0x00,0x00,0xFF); Globals::SystemColors [5] = RGB(0x00,0x00,0x80); Globals::SystemColors [6] = RGB(0x00,0xFF,0x00); Globals::SystemColors [7] = RGB(0x00,0x80,0x00); Globals::SystemColors [8] = RGB(0xFF,0x00,0x00); Globals::SystemColors [9] = RGB(0x80,0x00,0x00); Globals::SystemColors[10] = RGB(0xFF,0xFF,0x00); Globals::SystemColors[11] = RGB(0x80,0x80,0x00); Globals::SystemColors[12] = RGB(0x00,0xFF,0xFF); Globals::SystemColors[13] = RGB(0x00,0x80,0x80); Globals::SystemColors[14] = RGB(0xFF,0x00,0xFF); Globals::SystemColors[15] = RGB(0x80,0x00,0x80); SysColorNotify(); // update last 4 colors if (Globals::IsNt) { HMODULE module = GetModuleHandle(TEXT("gdi32.dll")); Globals::ExtTextOutFunction = (EXTTEXTOUTFUNCTION) GetProcAddress(module, "ExtTextOutW"); Globals::GdiIsMetaPrintDCFunction = (GDIISMETAPRINTDCFUNCTION) GetProcAddress(module, "GdiIsMetaPrintDC"); } else { HMODULE module = GetModuleHandleA("gdi32.dll"); Globals::ExtTextOutFunction = (EXTTEXTOUTFUNCTION) GetProcAddress(module, "ExtTextOutA"); Globals::GdiIsMetaPrintDCFunction = GdiIsMetaPrintDCWin9x; } Globals::LanguageID = GetLanguageID(); if (!InitSystemFontsDirs()) goto ErrorOut; // globals are initialized to NULL ASSERT(Globals::NationalDigitCache == NULL); Globals::UserDigitSubstituteInvalid = TRUE; Globals::CurrentSystemRenderingHintInvalid = TRUE; Globals::CurrentSystemRenderingHint = TextRenderingHintSingleBitPerPixelGridFit; VERBOSE(("Loading fonts...")); Globals::FontCollection = GpInstalledFontCollection::GetGpInstalledFontCollection(); if (!Globals::FontCollection || !(Globals::FontCollection->GetFontTable())) goto ErrorOut; // font caching, least recently used list Globals::FontCacheLastRecentlyUsedList = new GpCacheFaceRealizationList; if (!Globals::FontCacheLastRecentlyUsedList) goto ErrorOut; // Initialize for font file cache criticalization __try { InitializeCriticalSection(&Globals::TextCriticalSection); Globals::TextCriticalSectionInitialized = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { // We couldn't allocate the criticalSection // Return an error goto ErrorOut; } // If allocation failures are on, start default failure rate GpDoneInitializeAllocFailureMode(); // Now that everything's initialized, it's safe to start the background // thread. (The danger: It may immediately receive a message, and the // message-handling code assumes that we've been initialized already.) if (!input->SuppressBackgroundThread) { if (!BackgroundThreadStartup()) { goto ErrorOut; } } #if GP_ICECAP>1 CommentMarkProfile(1, "InternalGdiplusStartup completed"); #endif VERBOSE(("InternalGdiplusStartup completed successfully")); return Ok; ErrorOut: WARNING(("InternalGdiplusStartup: Initialization failed")); // Note that the following should free anything we've stuck in // the 'globals' class: InternalGdiplusShutdown(); return GenericError; } /**************************************************************************\ * * Function Description: * * Initialize font directory goop. * * Return Value: * * FALSE if failure (such as low memory). * * History: * * 6/10/1999 bodind * Created it. * * 3/1/2002 mikhaill - fixed bug 556954 \**************************************************************************/ #define numberof(x) (sizeof(x)/sizeof(x[0])) BOOL InitSystemFontsDirs(void) { // Check if already initialized if (Globals::FontsDirW) return TRUE; WCHAR windowsStr[MAX_PATH+1]; UINT windowsStrLength; // size of windows directory path, placed in windowsStr, // without trailing zero, in wide chars if (Globals::IsNt) { // GetWindowsDirectoryW is not working with TS // Also GetSystemWidowsDirectoryW is not supported in NT4 // So we only can use GetSystemDirectory and truncated system32 windowsStrLength = GetSystemDirectoryW(windowsStr, numberof(windowsStr)); if (windowsStrLength <= 0 || windowsStrLength >= numberof(windowsStr)) return FALSE; for (INT i = windowsStrLength - 1; i >= 0; i--) { if (windowsStr[i] == L'\\') { windowsStrLength = (UINT)i; break; } } } else { CHAR windowsStrA[numberof(windowsStr)]; windowsStrLength = GetWindowsDirectoryA(windowsStrA, numberof(windowsStrA)); if (windowsStrLength <= 0 || windowsStrLength >= numberof(windowsStrA)) return FALSE; int r = MultiByteToWideChar(CP_ACP, 0, windowsStrA, -1, windowsStr, numberof(windowsStr)); if (r == 0) return FALSE; // (mikhaill) The following check for trailing '\\' was inherited from old code. // Seems to be useless, but harmless. // In theory, can serve some weird situations, say, when // GetWindowsDirectory() returns "C:\\". if ((windowsStr[windowsStrLength - 1] == L'\\')) { windowsStrLength -= 1; } } if (windowsStrLength == 0) return FALSE; // Compute the font directory pathname length, including NULL static const WCHAR FontsSubdirW[] = L"\\fonts"; UINT fontsTotalLength = windowsStrLength + numberof(FontsSubdirW); // compose unicode path Globals::FontsDirW = new WCHAR[fontsTotalLength]; if (!Globals::FontsDirW) return FALSE; memcpy(Globals::FontsDirW, windowsStr, windowsStrLength*sizeof(WCHAR)); memcpy(Globals::FontsDirW + windowsStrLength, FontsSubdirW, sizeof(FontsSubdirW)); // make ansi path from unicode: // get the required buffer length int ansiLng = WideCharToMultiByte(CP_ACP, 0, Globals::FontsDirW, -1, NULL, 0, 0, 0); if (ansiLng == 0) goto fail; Globals::FontsDirA = new CHAR[ansiLng]; if (!Globals::FontsDirA) goto fail; int ansiLn2 = WideCharToMultiByte(CP_ACP, 0, Globals::FontsDirW, -1, Globals::FontsDirA, ansiLng, 0, 0); if (ansiLn2 != ansiLng) goto fail; #if INITIALIZE_DBG TERSE(("Fonts path is %ws (%d chars).", Globals::FontsDirW, fontsTotalLength)); #endif return TRUE; fail: delete[] Globals::FontsDirW; Globals::FontsDirW = NULL; delete[] Globals::FontsDirA; Globals::FontsDirA = NULL; return FALSE; } /**************************************************************************\ * * Function Description: * * Initializes direct draw and direct 3D related globals. * * Arguments: * * NONE * * Return Value: * * TRUE for success otherwise FALSE. * * History: * * 10/06/1999 bhouse * Created it. * \**************************************************************************/ BOOL InitializeDirectDrawGlobals(void) { if(Globals::DirectDrawInitialized) return TRUE; if(Globals::DirectDrawInitAttempted) return FALSE; // This critical section is used to protect LoadLibrary calls. LoadLibraryCriticalSection llcs; Globals::DirectDrawInitAttempted = TRUE; Globals::DdrawHandle = LoadLibraryA("ddraw.dll"); if(Globals::DdrawHandle == NULL) { WARNING(("Unable to load direct draw library")); return(FALSE); } Globals::GetDdrawSurfaceFromDcFunction = (GETDDRAWSURFACEFROMDCFUNCTION) GetProcAddress(Globals::DdrawHandle, "GetSurfaceFromDC"); if(Globals::GetDdrawSurfaceFromDcFunction == NULL) { WARNING(("Unable to get GetSurfaceFromDC procedure address")); return(FALSE); } Globals::DirectDrawCreateExFunction = (DIRECTDRAWCREATEEXFUNCTION) GetProcAddress(Globals::DdrawHandle, "DirectDrawCreateEx"); if(Globals::DirectDrawCreateExFunction == NULL) { WARNING(("Unable to get DirectDrawCreateEx procedure address")); return(FALSE); } Globals::DirectDrawEnumerateExFunction = (DIRECTDRAWENUMERATEEXFUNCTION) GetProcAddress(Globals::DdrawHandle, "DirectDrawEnumerateExA"); if(Globals::DirectDrawEnumerateExFunction == NULL) { WARNING(("Unable to get DirectDrawEnumerateEx procedure address")); return(FALSE); } HRESULT hr; hr = Globals::DirectDrawCreateExFunction(NULL, &Globals::DirectDraw, IID_IDirectDraw7, NULL); if(hr != DD_OK) { WARNING(("Unable to create Direct Draw interface")); return(FALSE); } hr = Globals::DirectDraw->SetCooperativeLevel(NULL, DDSCL_NORMAL); if(hr != DD_OK) { WARNING(("Unable to set DDSCL_NORMAL cooperative level")); return(FALSE); } hr = Globals::DirectDraw->QueryInterface(IID_IDirect3D7, (void **) &Globals::Direct3D); if(hr != DD_OK) { WARNING(("Unable to get D3D interface")); return(FALSE); } Globals::DirectDrawInitialized = TRUE; return TRUE; }