text: //------------------------------------------------------------------------------
  // File: VideoCtl.cpp
  //
  // Desc: DirectShow base classes.
  //
  // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  //------------------------------------------------------------------------------
  
  
  #include "streams.h"
  #include "ddmm.h"
  
  // Load a string from the resource file string table. The buffer must be at
  // least STR_MAX_LENGTH bytes. The easiest way to use this is to declare a
  // buffer in the property page class and use it for all string loading. It
  // cannot be static as multiple property pages may be active simultaneously
  
  LPTSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPTSTR pBuffer, int iResourceID)
  {
      if (LoadString(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) {
          return TEXT("");
      }
      return pBuffer;
  }
  
  #ifdef UNICODE
  LPSTR WINAPI StringFromResource(__out_ecount(STR_MAX_LENGTH) LPSTR pBuffer, int iResourceID)
  {
      if (LoadStringA(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH) == 0) {
          return "";
      }
      return pBuffer;
  }
  #endif
  
  
  
  // Property pages typically are called through their OLE interfaces. These
  // use UNICODE strings regardless of how the binary is built. So when we
  // load strings from the resource file we sometimes want to convert them
  // to UNICODE. This method is passed the target UNICODE buffer and does a
  // convert after loading the string (if built UNICODE this is not needed)
  // On WinNT we can explicitly call LoadStringW which saves two conversions
  
  #ifndef UNICODE
  
  LPWSTR WINAPI WideStringFromResource(__out_ecount(STR_MAX_LENGTH) LPWSTR pBuffer, int iResourceID)
  {
      *pBuffer = 0;
  
      if (g_amPlatform == VER_PLATFORM_WIN32_NT) {
  	LoadStringW(g_hInst,iResourceID,pBuffer,STR_MAX_LENGTH);
      } else {
  
  	CHAR szBuffer[STR_MAX_LENGTH];
  	DWORD dwStringLength = LoadString(g_hInst,iResourceID,szBuffer,STR_MAX_LENGTH);
  	// if we loaded a string convert it to wide characters, ensuring
  	// that we also null terminate the result.
  	if (dwStringLength++) {
  	    MultiByteToWideChar(CP_ACP,0,szBuffer,dwStringLength,pBuffer,STR_MAX_LENGTH);
  	}
      }
      return pBuffer;
  }
  
  #endif
  
  
  // Helper function to calculate the size of the dialog
  
  BOOL WINAPI GetDialogSize(int iResourceID,
                            DLGPROC pDlgProc,
                            LPARAM lParam,
                            __out SIZE *pResult)
  {
      RECT rc;
      HWND hwnd;
  
      // Create a temporary property page
  
      hwnd = CreateDialogParam(g_hInst,
                               MAKEINTRESOURCE(iResourceID),
                               GetDesktopWindow(),
                               pDlgProc,
                               lParam);
      if (hwnd == NULL) {
          return FALSE;
      }
  
      GetWindowRect(hwnd, &rc);
      pResult->cx = rc.right - rc.left;
      pResult->cy = rc.bottom - rc.top;
  
      DestroyWindow(hwnd);
      return TRUE;
  }
  
  
  // Class that aggregates on the IDirectDraw interface. Although DirectDraw
  // has the ability in its interfaces to be aggregated they're not currently
  // implemented. This makes it difficult for various parts of Quartz that want
  // to aggregate these interfaces. In particular the video renderer passes out
  // media samples that expose IDirectDraw and IDirectDrawSurface. The filter
  // graph manager also exposes IDirectDraw as a plug in distributor. For these
  // objects we provide these aggregation classes that republish the interfaces
  
  STDMETHODIMP CAggDirectDraw::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
  {
      ASSERT(m_pDirectDraw);
  
      // Do we have this interface
  
      if (riid == IID_IDirectDraw) {
          return GetInterface((IDirectDraw *)this,ppv);
      } else {
          return CUnknown::NonDelegatingQueryInterface(riid,ppv);
      }
  }
  
  
  STDMETHODIMP CAggDirectDraw::Compact()
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->Compact();
  }
  
  
  STDMETHODIMP CAggDirectDraw::CreateClipper(DWORD dwFlags, __deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper, __inout_opt IUnknown *pUnkOuter)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->CreateClipper(dwFlags,lplpDDClipper,pUnkOuter);
  }
  
  
  STDMETHODIMP CAggDirectDraw::CreatePalette(DWORD dwFlags,
                                             __in LPPALETTEENTRY lpColorTable,
                                             __deref_out LPDIRECTDRAWPALETTE *lplpDDPalette,
                                             __inout_opt IUnknown *pUnkOuter)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->CreatePalette(dwFlags,lpColorTable,lplpDDPalette,pUnkOuter);
  }
  
  
  STDMETHODIMP CAggDirectDraw::CreateSurface(__in LPDDSURFACEDESC lpDDSurfaceDesc,
                                             __deref_out LPDIRECTDRAWSURFACE *lplpDDSurface,
                                             __inout_opt IUnknown *pUnkOuter)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->CreateSurface(lpDDSurfaceDesc,lplpDDSurface,pUnkOuter);
  }
  
  
  STDMETHODIMP CAggDirectDraw::DuplicateSurface(__in LPDIRECTDRAWSURFACE lpDDSurface,
                                                __deref_out LPDIRECTDRAWSURFACE *lplpDupDDSurface)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->DuplicateSurface(lpDDSurface,lplpDupDDSurface);
  }
  
  
  STDMETHODIMP CAggDirectDraw::EnumDisplayModes(DWORD dwSurfaceDescCount,
                                                __in LPDDSURFACEDESC lplpDDSurfaceDescList,
                                                __in LPVOID lpContext,
                                                __in LPDDENUMMODESCALLBACK lpEnumCallback)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->EnumDisplayModes(dwSurfaceDescCount,lplpDDSurfaceDescList,lpContext,lpEnumCallback);
  }
  
  
  STDMETHODIMP CAggDirectDraw::EnumSurfaces(DWORD dwFlags,
                                            __in LPDDSURFACEDESC lpDDSD,
                                            __in LPVOID lpContext,
                                            __in LPDDENUMSURFACESCALLBACK lpEnumCallback)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->EnumSurfaces(dwFlags,lpDDSD,lpContext,lpEnumCallback);
  }
  
  
  STDMETHODIMP CAggDirectDraw::FlipToGDISurface()
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->FlipToGDISurface();
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetCaps(__out LPDDCAPS lpDDDriverCaps,__out LPDDCAPS lpDDHELCaps)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetCaps(lpDDDriverCaps,lpDDHELCaps);
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetDisplayMode(__out LPDDSURFACEDESC lpDDSurfaceDesc)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetDisplayMode(lpDDSurfaceDesc);
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetFourCCCodes(__inout LPDWORD lpNumCodes,__out_ecount(*lpNumCodes) LPDWORD lpCodes)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetFourCCCodes(lpNumCodes,lpCodes);
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetGDISurface(__deref_out LPDIRECTDRAWSURFACE *lplpGDIDDSurface)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetGDISurface(lplpGDIDDSurface);
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetMonitorFrequency(__out LPDWORD lpdwFrequency)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetMonitorFrequency(lpdwFrequency);
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetScanLine(__out LPDWORD lpdwScanLine)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetScanLine(lpdwScanLine);
  }
  
  
  STDMETHODIMP CAggDirectDraw::GetVerticalBlankStatus(__out LPBOOL lpblsInVB)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->GetVerticalBlankStatus(lpblsInVB);
  }
  
  
  STDMETHODIMP CAggDirectDraw::Initialize(__in GUID *lpGUID)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->Initialize(lpGUID);
  }
  
  
  STDMETHODIMP CAggDirectDraw::RestoreDisplayMode()
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->RestoreDisplayMode();
  }
  
  
  STDMETHODIMP CAggDirectDraw::SetCooperativeLevel(HWND hWnd,DWORD dwFlags)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->SetCooperativeLevel(hWnd,dwFlags);
  }
  
  
  STDMETHODIMP CAggDirectDraw::SetDisplayMode(DWORD dwWidth,DWORD dwHeight,DWORD dwBpp)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->SetDisplayMode(dwWidth,dwHeight,dwBpp);
  }
  
  
  STDMETHODIMP CAggDirectDraw::WaitForVerticalBlank(DWORD dwFlags,HANDLE hEvent)
  {
      ASSERT(m_pDirectDraw);
      return m_pDirectDraw->WaitForVerticalBlank(dwFlags,hEvent);
  }
  
  
  // Class that aggregates an IDirectDrawSurface interface. Although DirectDraw
  // has the ability in its interfaces to be aggregated they're not currently
  // implemented. This makes it difficult for various parts of Quartz that want
  // to aggregate these interfaces. In particular the video renderer passes out
  // media samples that expose IDirectDraw and IDirectDrawSurface. The filter
  // graph manager also exposes IDirectDraw as a plug in distributor. For these
  // objects we provide these aggregation classes that republish the interfaces
  
  STDMETHODIMP CAggDrawSurface::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
  {
      ASSERT(m_pDirectDrawSurface);
  
      // Do we have this interface
  
      if (riid == IID_IDirectDrawSurface) {
          return GetInterface((IDirectDrawSurface *)this,ppv);
      } else {
          return CUnknown::NonDelegatingQueryInterface(riid,ppv);
      }
  }
  
  
  STDMETHODIMP CAggDrawSurface::AddAttachedSurface(__in LPDIRECTDRAWSURFACE lpDDSAttachedSurface)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->AddAttachedSurface(lpDDSAttachedSurface);
  }
  
  
  STDMETHODIMP CAggDrawSurface::AddOverlayDirtyRect(__in LPRECT lpRect)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->AddOverlayDirtyRect(lpRect);
  }
  
  
  STDMETHODIMP CAggDrawSurface::Blt(__in LPRECT lpDestRect,
                                    __in LPDIRECTDRAWSURFACE lpDDSrcSurface,
                                    __in LPRECT lpSrcRect,
                                    DWORD dwFlags,
                                    __in LPDDBLTFX lpDDBltFx)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->Blt(lpDestRect,lpDDSrcSurface,lpSrcRect,dwFlags,lpDDBltFx);
  }
  
  
  STDMETHODIMP CAggDrawSurface::BltBatch(__in_ecount(dwCount) LPDDBLTBATCH lpDDBltBatch,DWORD dwCount,DWORD dwFlags)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->BltBatch(lpDDBltBatch,dwCount,dwFlags);
  }
  
  
  STDMETHODIMP CAggDrawSurface::BltFast(DWORD dwX,DWORD dwY,
                                        __in LPDIRECTDRAWSURFACE lpDDSrcSurface,
                                        __in LPRECT lpSrcRect,
                                        DWORD dwTrans)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->BltFast(dwX,dwY,lpDDSrcSurface,lpSrcRect,dwTrans);
  }
  
  
  STDMETHODIMP CAggDrawSurface::DeleteAttachedSurface(DWORD dwFlags,
                                                      __in LPDIRECTDRAWSURFACE lpDDSAttachedSurface)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->DeleteAttachedSurface(dwFlags,lpDDSAttachedSurface);
  }
  
  
  STDMETHODIMP CAggDrawSurface::EnumAttachedSurfaces(__in LPVOID lpContext,
                                                     __in LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->EnumAttachedSurfaces(lpContext,lpEnumSurfacesCallback);
  }
  
  
  STDMETHODIMP CAggDrawSurface::EnumOverlayZOrders(DWORD dwFlags,
                                                   __in LPVOID lpContext,
                                                   __in LPDDENUMSURFACESCALLBACK lpfnCallback)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->EnumOverlayZOrders(dwFlags,lpContext,lpfnCallback);
  }
  
  
  STDMETHODIMP CAggDrawSurface::Flip(__in LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride,DWORD dwFlags)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->Flip(lpDDSurfaceTargetOverride,dwFlags);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetAttachedSurface(__in LPDDSCAPS lpDDSCaps,
                                                   __deref_out LPDIRECTDRAWSURFACE *lplpDDAttachedSurface)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetAttachedSurface(lpDDSCaps,lplpDDAttachedSurface);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetBltStatus(DWORD dwFlags)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetBltStatus(dwFlags);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetCaps(__out LPDDSCAPS lpDDSCaps)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetCaps(lpDDSCaps);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetClipper(__deref_out LPDIRECTDRAWCLIPPER *lplpDDClipper)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetClipper(lplpDDClipper);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetColorKey(DWORD dwFlags,__out LPDDCOLORKEY lpDDColorKey)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetColorKey(dwFlags,lpDDColorKey);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetDC(__out HDC *lphDC)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetDC(lphDC);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetFlipStatus(DWORD dwFlags)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetFlipStatus(dwFlags);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetOverlayPosition(__out LPLONG lpdwX,__out LPLONG lpdwY)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetOverlayPosition(lpdwX,lpdwY);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetPalette(__deref_out LPDIRECTDRAWPALETTE *lplpDDPalette)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetPalette(lplpDDPalette);
  }
  
  
  STDMETHODIMP CAggDrawSurface::GetPixelFormat(__out LPDDPIXELFORMAT lpDDPixelFormat)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->GetPixelFormat(lpDDPixelFormat);
  }
  
  
  // A bit of a warning here: Our media samples in DirectShow aggregate on
  // IDirectDraw and IDirectDrawSurface (ie are available through IMediaSample
  // by QueryInterface). Unfortunately the underlying DirectDraw code cannot
  // be aggregated so we have to use these classes. The snag is that when we
  // call a different surface and pass in this interface as perhaps the source
  // surface the call will fail because DirectDraw dereferences the pointer to
  // get at its private data structures. Therefore we supply this workaround to give
  // access to the real IDirectDraw surface. A filter can call GetSurfaceDesc
  // and we will fill in the lpSurface pointer with the real underlying surface
  
  STDMETHODIMP CAggDrawSurface::GetSurfaceDesc(__out LPDDSURFACEDESC lpDDSurfaceDesc)
  {
      ASSERT(m_pDirectDrawSurface);
  
      // First call down to the underlying DirectDraw
  
      HRESULT hr = m_pDirectDrawSurface->GetSurfaceDesc(lpDDSurfaceDesc);
      if (FAILED(hr)) {
          return hr;
      }
  
      // Store the real DirectDrawSurface interface
      lpDDSurfaceDesc->lpSurface = m_pDirectDrawSurface;
      return hr;
  }
  
  
  STDMETHODIMP CAggDrawSurface::Initialize(__in LPDIRECTDRAW lpDD,__in LPDDSURFACEDESC lpDDSurfaceDesc)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->Initialize(lpDD,lpDDSurfaceDesc);
  }
  
  
  STDMETHODIMP CAggDrawSurface::IsLost()
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->IsLost();
  }
  
  
  STDMETHODIMP CAggDrawSurface::Lock(__in LPRECT lpDestRect,
                                     __inout LPDDSURFACEDESC lpDDSurfaceDesc,
                                     DWORD dwFlags,
                                     HANDLE hEvent)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->Lock(lpDestRect,lpDDSurfaceDesc,dwFlags,hEvent);
  }
  
  
  STDMETHODIMP CAggDrawSurface::ReleaseDC(HDC hDC)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->ReleaseDC(hDC);
  }
  
  
  STDMETHODIMP CAggDrawSurface::Restore()
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->Restore();
  }
  
  
  STDMETHODIMP CAggDrawSurface::SetClipper(__in LPDIRECTDRAWCLIPPER lpDDClipper)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->SetClipper(lpDDClipper);
  }
  
  
  STDMETHODIMP CAggDrawSurface::SetColorKey(DWORD dwFlags,__in LPDDCOLORKEY lpDDColorKey)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->SetColorKey(dwFlags,lpDDColorKey);
  }
  
  
  STDMETHODIMP CAggDrawSurface::SetOverlayPosition(LONG dwX,LONG dwY)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->SetOverlayPosition(dwX,dwY);
  }
  
  
  STDMETHODIMP CAggDrawSurface::SetPalette(__in LPDIRECTDRAWPALETTE lpDDPalette)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->SetPalette(lpDDPalette);
  }
  
  
  STDMETHODIMP CAggDrawSurface::Unlock(__in LPVOID lpSurfaceData)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->Unlock(lpSurfaceData);
  }
  
  
  STDMETHODIMP CAggDrawSurface::UpdateOverlay(__in LPRECT lpSrcRect,
                                              __in LPDIRECTDRAWSURFACE lpDDDestSurface,
                                              __in LPRECT lpDestRect,
                                              DWORD dwFlags,
                                              __in LPDDOVERLAYFX lpDDOverlayFX)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->UpdateOverlay(lpSrcRect,lpDDDestSurface,lpDestRect,dwFlags,lpDDOverlayFX);
  }
  
  
  STDMETHODIMP CAggDrawSurface::UpdateOverlayDisplay(DWORD dwFlags)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->UpdateOverlayDisplay(dwFlags);
  }
  
  
  STDMETHODIMP CAggDrawSurface::UpdateOverlayZOrder(DWORD dwFlags,__in LPDIRECTDRAWSURFACE lpDDSReference)
  {
      ASSERT(m_pDirectDrawSurface);
      return m_pDirectDrawSurface->UpdateOverlayZOrder(dwFlags,lpDDSReference);
  }
  
  
  // DirectShow must work on multiple platforms.  In particular, it also runs on
  // Windows NT 3.51 which does not have DirectDraw capabilities. The filters
  // cannot therefore link statically to the DirectDraw library. To make their
  // lives that little bit easier we provide this class that manages loading
  // and unloading the library and creating the initial IDirectDraw interface
  
  CLoadDirectDraw::CLoadDirectDraw() :
      m_pDirectDraw(NULL),
      m_hDirectDraw(NULL)
  {
  }
  
  
  // Destructor forces unload
  
  CLoadDirectDraw::~CLoadDirectDraw()
  {
      ReleaseDirectDraw();
  
      if (m_hDirectDraw) {
          NOTE("Unloading library");
          FreeLibrary(m_hDirectDraw);
      }
  }
  
  
  // We can't be sure that DirectDraw is always available so we can't statically
  // link to the library. Therefore we load the library, get the function entry
  // point addresses and call them to create the driver objects. We return S_OK
  // if we manage to load DirectDraw correctly otherwise we return E_NOINTERFACE
  // We initialise a DirectDraw instance by explicitely loading the library and
  // calling GetProcAddress on the DirectDrawCreate entry point that it exports
  
  // On a multi monitor system, we can get the DirectDraw object for any
  // monitor (device) with the optional szDevice parameter
  
  HRESULT CLoadDirectDraw::LoadDirectDraw(__in LPSTR szDevice)
  {
      PDRAWCREATE pDrawCreate;
      PDRAWENUM pDrawEnum;
      LPDIRECTDRAWENUMERATEEXA pDrawEnumEx;
  
      NOTE("Entering DoLoadDirectDraw");
  
      // Is DirectDraw already loaded
  
      if (m_pDirectDraw) {
          NOTE("Already loaded");
          ASSERT(m_hDirectDraw);
          return NOERROR;
      }
  
      // Make sure the library is available
  
      if(!m_hDirectDraw)
      {
          UINT ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX);
          m_hDirectDraw = LoadLibrary(TEXT("DDRAW.DLL"));
          SetErrorMode(ErrorMode);
  
          if (m_hDirectDraw == NULL) {
              DbgLog((LOG_ERROR,1,TEXT("Can't load DDRAW.DLL")));
              NOTE("No library");
              return E_NOINTERFACE;
          }
      }
  
      // Get the DLL address for the creator function
  
      pDrawCreate = (PDRAWCREATE)GetProcAddress(m_hDirectDraw,"DirectDrawCreate");
      // force ANSI, we assume it
      pDrawEnum = (PDRAWENUM)GetProcAddress(m_hDirectDraw,"DirectDrawEnumerateA");
      pDrawEnumEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress(m_hDirectDraw,
  						"DirectDrawEnumerateExA");
  
      // We don't NEED DirectDrawEnumerateEx, that's just for multimon stuff
      if (pDrawCreate == NULL || pDrawEnum == NULL) {
          DbgLog((LOG_ERROR,1,TEXT("Can't get functions: Create=%x Enum=%x"),
  			pDrawCreate, pDrawEnum));
          NOTE("No entry point");
          ReleaseDirectDraw();
          return E_NOINTERFACE;
      }
  
      DbgLog((LOG_TRACE,3,TEXT("Creating DDraw for device %s"),
  					szDevice ? szDevice : "<NULL>"));
  
      // Create a DirectDraw display provider for this device, using the fancy
      // multimon-aware version, if it exists
      if (pDrawEnumEx)
          m_pDirectDraw = DirectDrawCreateFromDeviceEx(szDevice, pDrawCreate,
  								pDrawEnumEx);
      else
          m_pDirectDraw = DirectDrawCreateFromDevice(szDevice, pDrawCreate,
  								pDrawEnum);
  
      if (m_pDirectDraw == NULL) {
              DbgLog((LOG_ERROR,1,TEXT("Can't create DDraw")));
              NOTE("No instance");
              ReleaseDirectDraw();
              return E_NOINTERFACE;
      }
      return NOERROR;
  }
  
  
  // Called to release any DirectDraw provider we previously loaded. We may be
  // called at any time especially when something goes horribly wrong and when
  // we need to clean up before returning so we can't guarantee that all state
  // variables are consistent so free only those really allocated allocated
  // This should only be called once all reference counts have been released
  
  void CLoadDirectDraw::ReleaseDirectDraw()
  {
      NOTE("Releasing DirectDraw driver");
  
      // Release any DirectDraw provider interface
  
      if (m_pDirectDraw) {
          NOTE("Releasing instance");
          m_pDirectDraw->Release();
          m_pDirectDraw = NULL;
      }
  
  }
  
  
  // Return NOERROR (S_OK) if DirectDraw has been loaded by this object
  
  HRESULT CLoadDirectDraw::IsDirectDrawLoaded()
  {
      NOTE("Entering IsDirectDrawLoaded");
  
      if (m_pDirectDraw == NULL) {
          NOTE("DirectDraw not loaded");
          return S_FALSE;
      }
      return NOERROR;
  }
  
  
  // Return the IDirectDraw interface we look after
  
  LPDIRECTDRAW CLoadDirectDraw::GetDirectDraw()
  {
      NOTE("Entering GetDirectDraw");
  
      if (m_pDirectDraw == NULL) {
          NOTE("No DirectDraw");
          return NULL;
      }
  
      NOTE("Returning DirectDraw");
      m_pDirectDraw->AddRef();
      return m_pDirectDraw;
  }
  
  
  // Are we running on Direct Draw version 1?  We need to find out as
  // we rely on specific bug fixes in DirectDraw 2 for fullscreen playback. To
  // find out, we simply see if it supports IDirectDraw2.  Only version 2 and
  // higher support this.
  
  BOOL CLoadDirectDraw::IsDirectDrawVersion1()
  {
  
      if (m_pDirectDraw == NULL)
  	return FALSE;
  
      IDirectDraw2 *p = NULL;
      HRESULT hr = m_pDirectDraw->QueryInterface(IID_IDirectDraw2, (void **)&p);
      if (p)
  	p->Release();
      if (hr == NOERROR) {
          DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 2 or greater")));
  	return FALSE;
      } else {
          DbgLog((LOG_TRACE,3,TEXT("Direct Draw Version 1")));
  	return TRUE;
      }
  }
  