text: ///////////////////////////////////////////////////////////////////////////////
  //
  //  File:  gfxui.c
  //
  //      This file defines the functions that are used by the Global
  //      Effects (GFX) page to drive manipulate the effects for a 
  //      mixer.
  //
  //  History:
  //      10 June 2000 RogerW
  //          Created.
  //
  //  Copyright (C) 2000 Microsoft Corporation  All Rights Reserved.
  //
  //                  Microsoft Confidential
  //
  ///////////////////////////////////////////////////////////////////////////////
  
  //=============================================================================
  //                            Include files
  //=============================================================================
  #include <windows.h>
  #include <windowsx.h>
  #include "mmcpl.h"
  #include <mmddkp.h>
  #include <olectl.h>
  #include <ocidl.h>
  #include "gfxui.h"
  
  #define ADDGFX
  #define REGSTR_VAL_FRIENDLYNAME TEXT("FriendlyName")
  
  
  //
  // IDataObject Implementation
  //
  class GFXDataObject : public IDataObject
  {
  public:
      GFXDataObject (DWORD dwGfxID) { m_cRef = 1; m_dwGfxID = dwGfxID; }
  
      // IUnknown interface
      STDMETHODIMP QueryInterface (REFIID riid, LPVOID *ppv);
      STDMETHODIMP_(ULONG) AddRef ()  { return ++m_cRef; }
      STDMETHODIMP_(ULONG) Release () { return --m_cRef; }
  
  	STDMETHODIMP GetData (FORMATETC * pformatetcIn, STGMEDIUM * pmedium);
      STDMETHODIMP GetDataHere (FORMATETC * pformatetc, STGMEDIUM *pmedium) { return E_NOTIMPL; }
  	STDMETHODIMP QueryGetData (FORMATETC * pformatetc) { return E_NOTIMPL; }
  	STDMETHODIMP GetCanonicalFormatEtc (FORMATETC * pformatetcIn, FORMATETC * pFormatetcOut) { return E_NOTIMPL; }
  	STDMETHODIMP SetData (FORMATETC * pformatetc, STGMEDIUM * pmedium, BOOL fRelease) { return E_NOTIMPL; }
  	STDMETHODIMP EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC ** ppenumFormatEtc ) { return E_NOTIMPL; }
  	STDMETHODIMP DAdvise (FORMATETC * pformatetc, DWORD advf, IAdviseSink* pAdvSnk, DWORD * pdwConnection) { return E_NOTIMPL; }
  	STDMETHODIMP DUnadvise (DWORD dwConnection) { return E_NOTIMPL; }
  	STDMETHODIMP EnumDAdvise (IEnumSTATDATA ** ppenumAdvise) { return E_NOTIMPL; }
  
  private:
  	UINT  m_cRef;
      DWORD m_dwGfxID;
  
  };
  
  STDMETHODIMP GFXDataObject::QueryInterface (REFIID riid, LPVOID * ppvObj)
  {
      
      if (!ppvObj)
          return E_POINTER;
  
  	*ppvObj = NULL;
  	if (IsEqualIID(riid,IID_IDataObject) ||
  		IsEqualIID(riid,IID_IUnknown))
  		*ppvObj = (IDataObject *) this;
  	else
  	    return E_NOINTERFACE;
  
  	((IUnknown *) *ppvObj) -> AddRef ();
  
  	return S_OK;
  
  }
  
  STDMETHODIMP GFXDataObject::GetData (LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)							// @parm Storage to be created.
  {
  
      HRESULT hr = E_INVALIDARG;
  
      if (pformatetc && pmedium && TYMED_HGLOBAL == pformatetc -> tymed)
      {
          HANDLE hGfx = NULL;
  
  #ifdef ADDGFX
          hr = gfxOpenGfx (m_dwGfxID, &hGfx);
  #endif // ADDGFX
  
          if (SUCCEEDED (hr))
          {
              pmedium -> tymed = TYMED_HGLOBAL;
  	        pmedium -> pUnkForRelease = NULL;	
  	        pmedium -> hGlobal = hGfx;
          }
      }
  
      return hr;
  
  }
  
  
  
  HRESULT InitList (DWORD dwMixID, DWORD dwType, PPGFXUILIST ppList)
  {
  
      HRESULT hr = E_INVALIDARG;
  
      if (ppList)
      {
          PGFXUILIST pList = (PGFXUILIST) LocalAlloc (LPTR, sizeof (GFXUILIST));
          *ppList = NULL; // Init pointer
  
          if (pList)
          {
              hr = S_OK;
  	        pList -> dwType    = dwType;
              pList -> puiList   = NULL;
  #ifdef UNICODE
              pList -> pszZoneDi = (PWSTR) GetInterfaceName (dwMixID);
  #else
              pList -> pszZoneDi = NULL; // This should not happen
  #endif
              if (pList -> pszZoneDi)
              {
                  // Return new list
                  *ppList = pList;
              }
              else
              {
                  // Error!
                  LocalFree (pList);
                  hr = E_OUTOFMEMORY;
              }
          }
          else
              hr = E_OUTOFMEMORY;
      }
  
      return hr;
  
  }
  
  
  void GFXUI_FreeList (PPGFXUILIST ppList)
  {
      
      if (ppList)
      {
          PGFXUILIST pList = *ppList;
  
          if (pList)
          {
              // Free Zone
              if (pList -> pszZoneDi)
                  GlobalFreePtr (pList -> pszZoneDi);
              pList -> pszZoneDi = NULL;
  
              // Free GFX List
              FreeListNodes (&(pList -> puiList));
  
              // Free list
              LocalFree (pList);
              *ppList = NULL;
          }
      }
  }
  
  
  void FreeListNodes (PPGFXUI ppuiList)
  {
  
      if (ppuiList)
      {
          PGFXUI pNodeDelete;
          PGFXUI puiList = *ppuiList;
  
          // Free list nodes
          while (puiList)
          {
              pNodeDelete = puiList;
              puiList = puiList -> pNext;
              FreeNode (&pNodeDelete);
          }
  
          *ppuiList = NULL;
      }
  }
  
  
  void FreeNode (PPGFXUI ppNode)
  {
  
      if (ppNode && *ppNode)
      {
          PGFXUI pNode = *ppNode;
  
          // Free the strings
          if (pNode -> pszName)
              LocalFree (pNode -> pszName);
          if (pNode -> pszFactoryDi)
              LocalFree (pNode -> pszFactoryDi);
  
          // Free the node
          LocalFree (pNode);
          *ppNode = NULL;
      }
  }
  
  
  HKEY OpenGfxRegKey (PCWSTR pszGfxFactoryDi, REGSAM sam)
  {
  
      HKEY hkeyGfx = NULL;
  
      if (pszGfxFactoryDi)
      {
          HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList (NULL, NULL); 
          
          if (INVALID_HANDLE_VALUE != DeviceInfoSet)
          {
              SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
              DeviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
  
              if (SetupDiOpenDeviceInterface (DeviceInfoSet, pszGfxFactoryDi, 
                                              0, &DeviceInterfaceData))
              {
                   hkeyGfx = SetupDiOpenDeviceInterfaceRegKey (
                                  DeviceInfoSet, &DeviceInterfaceData,
                                  0, sam);
              }
              SetupDiDestroyDeviceInfoList (DeviceInfoSet);
          }
      }
  
      return hkeyGfx;
  
  }
  
  
  HRESULT GetFriendlyName (PCWSTR pszGfxFactoryDi, PWSTR* ppszName)
  {
  
      HRESULT hr = E_INVALIDARG;
      HKEY hkeyGfx = NULL;
  
      // Check parameters
      if (ppszName && pszGfxFactoryDi)
      {
          HKEY hkeyGfx = OpenGfxRegKey (pszGfxFactoryDi, KEY_READ);
          *ppszName = NULL;
  
          if (hkeyGfx)
          {
              WCHAR szBuffer[MAX_PATH];
              DWORD dwType = REG_SZ;
              DWORD cb     = sizeof (szBuffer) / sizeof (szBuffer[0]);
  
              hr = S_OK;
  
              if (ERROR_SUCCESS == RegQueryValueEx (hkeyGfx, REGSTR_VAL_FRIENDLYNAME, NULL, &dwType, (LPBYTE)szBuffer, &cb))
              {
                  *ppszName = (PWSTR) LocalAlloc (LPTR, lstrlen (szBuffer)*2+2);
                  if (*ppszName)
                      wcscpy (*ppszName, szBuffer);
                  else
                      hr = E_OUTOFMEMORY;
              }
              else
                  hr = REGDB_E_READREGDB;
  
              RegCloseKey (hkeyGfx);
          }
      }
  
      return hr;
  
  }
  
  
  HRESULT AddFactoryNode (PCWSTR pszGfxFactoryDi, PPGFXUILIST ppList)
  {
      return AddNode (pszGfxFactoryDi, 0, GUID_NULL, 0, 0, 0, ppList);
  }
  
  
  HRESULT AddNode (PCWSTR pszGfxFactoryDi, DWORD Id, REFCLSID rclsid, DWORD Type, 
                   DWORD Order, DWORD nFlags, PPGFXUILIST ppList)
  {
  
      PGFXUI pNode = NULL;
      HRESULT hr = S_OK;
  
      // Check parameters
      if (!ppList || !(*ppList) || !pszGfxFactoryDi)
          return E_INVALIDARG;
  
      // Create node
      if (SUCCEEDED (hr = CreateNode (NULL, pszGfxFactoryDi, &pNode)))
      {
          if (pNode)
          {
              // Initilize the rest of the values
              pNode -> Id      = Id; 
              pNode -> Type    = Type;
              pNode -> Order   = Order;
              pNode -> nFlags  = nFlags;
              pNode -> clsidUI = rclsid;
  
              if (FAILED (hr = AttachNode (ppList, pNode)))
                  FreeNode (&pNode);
          }
          else
              hr = E_UNEXPECTED;
  
      }
  
      return hr;
  
  }
  
  
  // Note: This function always adds the node to the list IN ORDER
  //       IFF (pNode -> nFlags & GFX_CREATED).
  HRESULT AttachNode (PPGFXUILIST ppList, PGFXUI pNode)
  {
  
      HRESULT hr = E_INVALIDARG;
  
      // Check parameters
      if (ppList && (*ppList) && pNode)
      {
          PGFXUI puiList = (*ppList) -> puiList;
          hr = S_OK;
  
          // Make sure our next pointer starts out null..
          pNode -> pNext = NULL;
  
          if (puiList)
          {
              if (!(pNode -> nFlags & GFX_CREATED) ||
                  (puiList -> Order >= pNode -> Order))
              {
                  // Order is not available, just stick it on the front or
                  // the order happens to put the node at the front.
                  pNode -> pNext = puiList;
                  puiList = pNode;
              }
              else
              {
                  PGFXUI pSearch = puiList;
              
                  if (!(puiList -> pNext))
                  {
                      // One element list. We know the new node doesn't belong
                      // at the head of the list, so it is behind.
                      puiList -> pNext = pNode;
                  } else {
  
                      while (pSearch -> pNext)
                      {
                          if (!(pSearch -> pNext -> nFlags & GFX_CREATED))
                          {
                              hr = E_INVALIDARG;
                              break; // Cannot mix list types
                          }
      
                          if (pSearch -> pNext -> Order >= pNode -> Order)
                          {
                              // We found the insertion point!
                              pNode -> pNext = pSearch -> pNext;
                              pSearch -> pNext = pNode;
                              break;
                          }
      
                          if (!(pSearch -> pNext -> pNext))
                          {
                              // At end of list, attach node to end
                              pSearch -> pNext -> pNext = pNode;
                              break;
                          }
      
                          // Move to next element
                          pSearch = pSearch -> pNext;
                      }
                      
                  }
              }
          }
          else
          {
              // First element of the list
              puiList = pNode;
          }
  
          if (SUCCEEDED (hr))
          {
              // Ensure we pass back the correct list pointer
              (*ppList) -> puiList = puiList;
          }
      }
  
      return hr;
  
  }
  
  
  LONG GFXEnum (PVOID Context, DWORD Id, PCWSTR GfxFactoryDi, REFCLSID rclsid, DWORD Type, DWORD Order)
  {
  
      PGFXUILIST pList = (PGFXUILIST) Context;
      HRESULT hr = E_INVALIDARG;
  
      if (pList)
      {
          if (Type == pList->dwType)
          {
              if (FAILED (hr = AddNode (GfxFactoryDi, Id, rclsid, Type, Order, GFX_CREATED, &pList)))
              {
                  // Error, free the list
                  GFXUI_FreeList (&pList);
                  Context = NULL;
              }
          }
          else hr = NOERROR;
      }
  
      return hr;
  
  }
  
  
  BOOL GFXUI_CheckDevice (DWORD dwMixID, DWORD dwType)
  {
  
      HRESULT     hr = S_OK;
      BOOL        fRet = FALSE;
      PGFXUILIST  pList = NULL;
      
      if (SUCCEEDED (hr = InitList (dwMixID, dwType, &pList)))
      {
          if (pList && pList -> pszZoneDi)
          {
              PDEVICEINTERFACELIST pdiList = NULL;                    
              LONG                 lResult = NO_ERROR;
                  
              lResult = gfxCreateGfxFactoriesList (pList -> pszZoneDi, &pdiList);
  
              if ((lResult == NO_ERROR) && pdiList)
              {
                  fRet = TRUE;
                  gfxDestroyDeviceInterfaceList (pdiList);
              }
              
              GFXUI_FreeList (&pList);
          }
  
      }
      return fRet;
  }
  
  
  HRESULT GFXUI_CreateList (DWORD dwMixID, DWORD dwType, BOOL fAll, PPGFXUILIST ppList)
  {
  
      HRESULT hr = E_INVALIDARG;
      
      if (ppList)
      {
          hr = S_OK;
  
          if (SUCCEEDED (hr = InitList (dwMixID, dwType, ppList)))
          {
              if (*ppList && (*ppList) -> pszZoneDi)
              {
                  if (!fAll)
                  {
                      hr = gfxEnumerateGfxs ((*ppList) -> pszZoneDi, GFXEnum, (*ppList));
                  }
                  else
                  {
                      PDEVICEINTERFACELIST pdiList = NULL;                    
  
                      hr = gfxCreateGfxFactoriesList ((*ppList) -> pszZoneDi, &pdiList);
  
                      if (SUCCEEDED (hr) && pdiList)
                      {
                          LONG lIndex;
                      
                          for (lIndex = 0; lIndex < pdiList -> Count; lIndex++)
                          {
                              hr = AddFactoryNode ((pdiList -> DeviceInterface)[lIndex], ppList);
                              if (FAILED (hr))
                              {
                                  GFXUI_FreeList (ppList);
                                  break;
                              }
                          }
  
                          gfxDestroyDeviceInterfaceList (pdiList);
                      }
                      else
                      {
                          GFXUI_FreeList (ppList);
                          hr = E_OUTOFMEMORY;
                      }
                  }
              }
              else
                  hr = E_UNEXPECTED;
          }
  
      }
  
      return hr;
  
  }
  
  
  HRESULT GFXUI_Properties (PGFXUI puiGFX, HWND hWndOwner)
  {
  
      HRESULT hr = E_INVALIDARG;
  
      if (puiGFX && GFXUI_CanShowProperties (puiGFX) && IsWindow (hWndOwner))
      {
          ISpecifyPropertyPages* pISpecifyPropertyPages = NULL;
  
          // Get the Vendor UI Property Pages Interface
          hr = CoCreateInstance (puiGFX -> clsidUI, NULL, CLSCTX_INPROC_SERVER, IID_ISpecifyPropertyPages, (void**)&pISpecifyPropertyPages);
          if (SUCCEEDED (hr) && !pISpecifyPropertyPages)
              hr = E_UNEXPECTED;
  
          if (SUCCEEDED (hr))
          {
              CAUUID Pages;
              ZeroMemory (&Pages, sizeof (Pages));
  
              // Get the VendorUI Property Page CLSID's
              hr = pISpecifyPropertyPages -> GetPages (&Pages);
              if (SUCCEEDED (hr) && (Pages.cElems == 0 || !Pages.pElems))
                  hr = E_UNEXPECTED;
  
              if (SUCCEEDED (hr))
              {
                  RECT rcWindow;
  
                  if (GetWindowRect (hWndOwner, &rcWindow))
                  {
                      TCHAR szCaption[MAX_PATH];
                      GFXDataObject DataObject (puiGFX -> Id);
                      IUnknown* punkDataObject = &DataObject;
  
                      // Load the VendorUI caption
                      LoadString (ghInstance, IDS_EFFECTS_PROPERTY_CAPTION, szCaption, sizeof (szCaption)/sizeof(TCHAR));
  
                      // Bring up the Vendor UI
                      hr = OleCreatePropertyFrame (hWndOwner, rcWindow.left + 10, rcWindow.top + 10,
                                                   szCaption, 1, &punkDataObject, Pages.cElems, 
                                                   Pages.pElems, GetSystemDefaultLangID (), 
                                                   0, NULL);
  
                  }
                  else
                      hr = E_FAIL;
  
                  CoTaskMemFree (Pages.pElems);
              }
  
              pISpecifyPropertyPages -> Release ();
          }
      }
  
      return hr;
  
  }
  
  
  UINT GetListSize (PGFXUI puiList)
  {
  
      UINT uiSize = 0;
      PGFXUI puiListSeek = puiList;
  
      while (puiListSeek)
      {
          puiListSeek = puiListSeek -> pNext;
          uiSize++;
      }
  
      return uiSize;
  
  }
  
  
  HRESULT GFXUI_Apply (PPGFXUILIST ppListApply, PPGFXUI ppuiListDelete)
  {
  
      HRESULT hr = E_INVALIDARG;
      PGFXUILIST pListApply  = (ppListApply  ? *ppListApply    : NULL);
      PGFXUI puiListDelete = (ppuiListDelete ? *ppuiListDelete : NULL);
  
      if ((pListApply && pListApply -> puiList) || puiListDelete)
      {
  
          PGFXREMOVEREQUEST paGfxRemoveRequests = NULL;
          PGFXMODIFYREQUEST paGfxModifyRequests = NULL;
          PGFXADDREQUEST    paGfxAddRequests    = NULL;
          ULONG cGfxRemoveRequests = 0;
  	    ULONG cGfxModifyRequests = 0;
  	    ULONG cGfxAddRequests    = 0;
          ULONG cApplyList = GetListSize (pListApply ? pListApply -> puiList : NULL);
          ULONG cDeleteList = GetListSize (puiListDelete);
          PGFXUI puiListSeek = NULL;
  
          hr = S_OK;
  
          // This function needs to create:
          //      - Deleted Array of GFXREMOVEREQUEST's
          //      - Modify Array of GFXMODIFYREQUEST's
          //      - Add Array of GFXADDREQUEST's
          // 
          // The deleted array is fairly easy, just pull out the 
          // marked ones. With the remaining, we need to loop through
          // them comparing order and create modify records as needed
          // to modify their order (except for the add records where
          // we must save the nessary order in the add request array).
          // Then we create the add array (perhaps with the modify array)
          // and fill in everything else (other than order).
          //
          // Then call GFX_BatchChange().
          //
          // Afterword, we need to update our list accordingly (i.e. delete 
          // nodes, update order, etc).
  
          // Create our parameters
          // Note: These buffers are really upper bounds on the memory we will need.
          //       We will count the actual requests we make and pass that value to
          //       the GFX function call.
          if (0 < cDeleteList)
          {
              paGfxRemoveRequests = (PGFXREMOVEREQUEST) LocalAlloc (LPTR, sizeof (*paGfxRemoveRequests) * cDeleteList);
              if (!paGfxRemoveRequests)
                  hr = E_OUTOFMEMORY;
          }
          if (0 < cApplyList)
          {
              paGfxModifyRequests = (PGFXMODIFYREQUEST) LocalAlloc (LPTR, sizeof (*paGfxModifyRequests) * cApplyList);
              paGfxAddRequests    = (PGFXADDREQUEST) LocalAlloc (LPTR, sizeof (*paGfxAddRequests) * cApplyList);
              if (!paGfxModifyRequests || !paGfxAddRequests)
                  hr = E_OUTOFMEMORY;
          }
  
          if (SUCCEEDED (hr))
          {
              UINT uiIndx;
              DWORD dwOrder = 0;
  
              // Create the remove parameter
              puiListSeek = puiListDelete;
              for (uiIndx = 0; uiIndx < cDeleteList; uiIndx++)
              {
                  // Make sure this is created before we ask to delete it.
                  // (It may be an AddNode that was deleted before creation).
                  if (puiListSeek -> nFlags & GFX_CREATED)
                  {
                      (paGfxRemoveRequests + cGfxRemoveRequests) -> IdToRemove = puiListSeek -> Id;
                      (paGfxRemoveRequests + cGfxRemoveRequests) -> Error = S_OK;
                      cGfxRemoveRequests++;
                  }
                  puiListSeek = puiListSeek -> pNext;
              }
  
              // Create the modify and add parameters
              puiListSeek = pListApply ? pListApply -> puiList : NULL;
              for (uiIndx = 0; uiIndx < cApplyList; uiIndx++)
              {
                  if (puiListSeek -> nFlags & GFX_ADD)
                  {
                      (paGfxAddRequests + cGfxAddRequests) -> ZoneFactoryDi = pListApply  -> pszZoneDi;
                      (paGfxAddRequests + cGfxAddRequests) -> GfxFactoryDi  = puiListSeek -> pszFactoryDi;
                      (paGfxAddRequests + cGfxAddRequests) -> Type          = pListApply  -> dwType;
                      (paGfxAddRequests + cGfxAddRequests) -> Order         = dwOrder++;
                      (paGfxAddRequests + cGfxAddRequests) -> NewId         = 0;
                      (paGfxAddRequests + cGfxAddRequests) -> Error         = S_OK;
                      cGfxAddRequests++;
                  }
                  else
                  {
                      if (puiListSeek -> nFlags & GFX_CREATED)
                      {
                          // We only need to add modify records for GFX'es
                          // that are no longer in order.
                          if (puiListSeek -> Order < dwOrder)
                          {
                              (paGfxModifyRequests + cGfxModifyRequests) -> IdToModify = puiListSeek -> Id;
                              (paGfxModifyRequests + cGfxModifyRequests) -> NewOrder   = dwOrder++;
                              (paGfxModifyRequests + cGfxModifyRequests) -> Error      = S_OK;
                              cGfxModifyRequests++;
                          }
                          else
                              dwOrder = (puiListSeek -> Order + 1);
                      }
                      else
                      {
                          // Bogus list entry, abort everything!
                          hr = E_INVALIDARG;
                          break;
                      }
                  }
                  puiListSeek = puiListSeek -> pNext;
              }
  
              if (SUCCEEDED (hr))
              {
  #ifdef ADDGFX
                  hr = gfxBatchChange (paGfxRemoveRequests, cGfxRemoveRequests,
                                       paGfxModifyRequests, cGfxModifyRequests,
                                       paGfxAddRequests, cGfxAddRequests);
  #endif // ADDGFX
                  if (SUCCEEDED (hr))
                  {
                      PGFXMODIFYREQUEST paGfxModifySeek = paGfxModifyRequests;
                      PGFXADDREQUEST    paGfxAddSeek    = paGfxAddRequests;
  
                      // Update the passed arrays
                      FreeListNodes (ppuiListDelete);
  
                      puiListSeek = pListApply ? pListApply -> puiList : NULL;
                      for (uiIndx = 0; uiIndx < cApplyList; uiIndx++)
                      {
                          // Update the list items.
                          if (puiListSeek -> nFlags & GFX_ADD)
                          {
                              // Update the newly create GFX
                              puiListSeek -> nFlags = GFX_CREATED;
                              puiListSeek -> Id     = paGfxAddSeek -> NewId;
                              puiListSeek -> Type   = paGfxAddSeek -> Type;
                              puiListSeek -> Order  = paGfxAddSeek -> Order;
                              paGfxAddSeek++;
                          }
                          else // must be (puiListSeek -> nFlags & GFX_CREATED)
                          {
                              // Update the order
                              puiListSeek -> Order = paGfxModifySeek -> NewOrder;
                              paGfxModifySeek++;
                          }
                      }
                  }
              }
          }
  
          // Free parameters
          if (paGfxRemoveRequests)
              LocalFree (paGfxRemoveRequests);
          if (paGfxModifyRequests)
              LocalFree (paGfxModifyRequests);
          if (paGfxAddRequests)
              LocalFree (paGfxAddRequests);
  
      }
  
      return hr;
  
  }
  
  
  ///////////////////////////////////////////////////////////////////////////////////////////
  // Microsoft Confidential - DO NOT COPY THIS METHOD INTO ANY APPLICATION, THIS MEANS YOU!!!
  ///////////////////////////////////////////////////////////////////////////////////////////
  PTCHAR GetInterfaceName (DWORD dwMixerID)
  {
  	MMRESULT mmr;
  	ULONG cbSize=0;
  	TCHAR *szInterfaceName=NULL;
  
  	//Query for the Device interface name
  	mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
  	if(MMSYSERR_NOERROR == mmr)
  	{
  		szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
  		if(!szInterfaceName)
  		{
  			return NULL;
  		}
  
  		mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
  		if(MMSYSERR_NOERROR != mmr)
  		{
  			GlobalFreePtr(szInterfaceName);
  			return NULL;
  		}
  	}
  
      return szInterfaceName;
  }
  
  
  BOOL GFXUI_CanShowProperties (PGFXUI puiGFX)
  {
      return (puiGFX && (puiGFX -> nFlags & GFX_CREATED) && (puiGFX -> clsidUI != GUID_NULL));
  }
  
  
  HRESULT CreateNode (PCWSTR pszName, PCWSTR pszGfxFactoryDi, PPGFXUI ppNode)
  {
  
      HRESULT hr = E_INVALIDARG;
      
      if (ppNode)
      {
          // Create node
          PGFXUI pNode = (PGFXUI) LocalAlloc (LPTR, sizeof (GFXUI));
          hr = S_OK;
  
          if (pNode)
          {
              ZeroMemory (pNode, sizeof (GFXUI));
  
              // Create the strings
              if (pszName)
              {
                  pNode -> pszName = (PWSTR) LocalAlloc (LPTR, lstrlen (pszName)*2+2);
                  if (pNode -> pszName)
                      wcscpy (pNode -> pszName, pszName);
                  else
                      hr = E_OUTOFMEMORY;
              }
              else
              {
                  // If there is no name, get it from the factory
                  pNode -> pszName = NULL;
                  if (pszGfxFactoryDi)
                      hr = GetFriendlyName (pszGfxFactoryDi, &(pNode -> pszName));
              }
  
              if (SUCCEEDED (hr) && pszGfxFactoryDi)
              {
                  pNode -> pszFactoryDi = (PWSTR) LocalAlloc (LPTR, lstrlen (pszGfxFactoryDi)*2+2);
                  if (pNode -> pszFactoryDi)
                      wcscpy (pNode -> pszFactoryDi, pszGfxFactoryDi);
                  else
                      hr = E_OUTOFMEMORY;
              }
  
              if (SUCCEEDED (hr))
                  // Return node
                  *ppNode = pNode;
              else
                  // Free node
                  FreeNode (&pNode);
          }
          else
              hr = E_OUTOFMEMORY;
      }
  
      return hr;
  }
  
  
  // This function creates an "addable" GFXUI element that will be able
  // to create a new GFX when you call GFXUI_Apply() with this new element
  // in the list.
  HRESULT GFXUI_CreateAddGFX (PPGFXUI ppuiGFXAdd, PGFXUI puiGFXSource)
  {
  
      HRESULT hr = E_INVALIDARG;
  
      if (ppuiGFXAdd && puiGFXSource)
      {
          *ppuiGFXAdd = NULL;
  
          hr = CreateNode (puiGFXSource -> pszName, 
                           puiGFXSource -> pszFactoryDi, ppuiGFXAdd);
  
          if (SUCCEEDED (hr))
          {
              if (*ppuiGFXAdd)
              {
                  // Indicate that this is a new 'Add' node.
                  (*ppuiGFXAdd) -> nFlags = GFX_ADD;
              }
              else
                  hr = E_UNEXPECTED;
          }
      }
  
      return hr;
  
  }
  