WindowsXP-SP1/shell/osshell/control/mmsys/gfxui.cpp
2020-09-30 16:53:49 +02:00

896 lines
26 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// 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;
}