/*
 * Implementation of CLSID_SystemDeviceEnum.
 * Implements IMoniker for CLSID_CDeviceMoniker.
 * Implements IPropertyBag. (internal)
 *
 * hidenori@a2.ctktv.ne.jp
 */

#include "config.h"

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winreg.h"
#include "winerror.h"
#include "objbase.h"
#include "oleauto.h"
#include "strmif.h"
#include "uuids.h"

#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(quartz);

#include "quartz_private.h"
#include "devenum.h"
#include "enumunk.h"
#include "complist.h"
#include "regsvr.h"

#ifndef NUMELEMS
#define NUMELEMS(elem)	(sizeof(elem)/sizeof(elem[0]))
#endif	/* NUMELEMS */

/***************************************************************************
 *
 *	new/delete for CLSID_SystemDeviceEnum
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry CSysDevEnum_IFEntries[] =
{
  { &IID_ICreateDevEnum, offsetof(CSysDevEnum,createdevenum)-offsetof(CSysDevEnum,unk) },
};


static void QUARTZ_DestroySystemDeviceEnum(IUnknown* punk)
{
	CSysDevEnum_THIS(punk,unk);

	CSysDevEnum_UninitICreateDevEnum( This );
}

HRESULT QUARTZ_CreateSystemDeviceEnum(IUnknown* punkOuter,void** ppobj)
{
	CSysDevEnum*	psde;
	HRESULT	hr;

	TRACE("(%p,%p)\n",punkOuter,ppobj);

	psde = (CSysDevEnum*)QUARTZ_AllocObj( sizeof(CSysDevEnum) );
	if ( psde == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &psde->unk, punkOuter );

	hr = CSysDevEnum_InitICreateDevEnum( psde );
	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj( psde );
		return hr;
	}

	psde->unk.pEntries = CSysDevEnum_IFEntries;
	psde->unk.dwEntries = sizeof(CSysDevEnum_IFEntries)/sizeof(CSysDevEnum_IFEntries[0]);
	psde->unk.pOnFinalRelease = QUARTZ_DestroySystemDeviceEnum;

	*ppobj = (void*)(&psde->unk);

	return S_OK;
}


/***************************************************************************
 *
 *	CSysDevEnum::ICreateDevEnum
 *
 */


static HRESULT WINAPI
ICreateDevEnum_fnQueryInterface(ICreateDevEnum* iface,REFIID riid,void** ppobj)
{
	CSysDevEnum_THIS(iface,createdevenum);

	TRACE("(%p)->()\n",This);

	return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
}

static ULONG WINAPI
ICreateDevEnum_fnAddRef(ICreateDevEnum* iface)
{
	CSysDevEnum_THIS(iface,createdevenum);

	TRACE("(%p)->()\n",This);

	return IUnknown_AddRef(This->unk.punkControl);
}

static ULONG WINAPI
ICreateDevEnum_fnRelease(ICreateDevEnum* iface)
{
	CSysDevEnum_THIS(iface,createdevenum);

	TRACE("(%p)->()\n",This);

	return IUnknown_Release(This->unk.punkControl);
}

static HRESULT WINAPI
ICreateDevEnum_fnCreateClassEnumerator(ICreateDevEnum* iface,REFCLSID rclsidDeviceClass,IEnumMoniker** ppobj, DWORD dwFlags)
{
	CSysDevEnum_THIS(iface,createdevenum);
	HRESULT	hr;
	HKEY	hKey;
	QUARTZ_CompList*	pMonList;
	IMoniker*	pMon;
	DWORD	dwIndex;
	LONG	lr;
	WCHAR	wszPath[ 1024 ];
	DWORD	dwLen;
	DWORD	dwNameMax;
	DWORD	cbName;
	FILETIME	ftLastWrite;

	TRACE("(%p)->(%s,%p,%08lx)\n",This,
		debugstr_guid(rclsidDeviceClass),ppobj,dwFlags);
	if ( dwFlags != 0 )
	{
		FIXME("unknown flags %08lx\n",dwFlags);
		return E_NOTIMPL;
	}

	if ( ppobj == NULL )
		return E_POINTER;
	*ppobj = NULL;

	hr = QUARTZ_CreateCLSIDPath(
		wszPath, sizeof(wszPath)/sizeof(wszPath[0]) - 16,
		rclsidDeviceClass, QUARTZ_wszInstance );
	if ( FAILED(hr) )
		return hr;

	lr = RegOpenKeyExW( HKEY_CLASSES_ROOT, wszPath,
		0, KEY_READ, &hKey );
	if ( lr != ERROR_SUCCESS )
	{
		TRACE("cannot open %s\n",debugstr_w(wszPath));
		if ( lr == ERROR_FILE_NOT_FOUND ||
			 lr == ERROR_PATH_NOT_FOUND )
			return S_FALSE;
		return E_FAIL;
	}

	dwLen = lstrlenW(wszPath);
	wszPath[dwLen++] = '\\'; wszPath[dwLen] = 0;
	dwNameMax = sizeof(wszPath)/sizeof(wszPath[0]) - dwLen - 8;

	pMonList = QUARTZ_CompList_Alloc();
	if ( pMonList == NULL )
	{
		hr = E_OUTOFMEMORY;
		goto err;
	}

	/* enumerate all subkeys. */
	dwIndex = 0;
	while ( 1 )
	{
		cbName = dwNameMax;
		lr = RegEnumKeyExW(
			hKey, dwIndex, &wszPath[dwLen], &cbName,
			NULL, NULL, NULL, &ftLastWrite );
		if ( lr == ERROR_NO_MORE_ITEMS )
			break;
		if ( lr != ERROR_SUCCESS )
		{
			TRACE("RegEnumKeyEx returns %08lx\n",lr);
			hr = E_FAIL;
			goto err;
		}

		hr = QUARTZ_CreateDeviceMoniker(
			HKEY_CLASSES_ROOT, wszPath, &pMon );
		if ( FAILED(hr) )
			goto err;

		hr = QUARTZ_CompList_AddComp(
			pMonList, (IUnknown*)pMon, NULL, 0 );
		IMoniker_Release( pMon );

		if ( FAILED(hr) )
			goto err;

		dwIndex ++;
	}

	/* create an enumerator. */
	hr = QUARTZ_CreateEnumUnknown(
		&IID_IEnumMoniker, (void**)ppobj, pMonList );
	if ( FAILED(hr) )
		goto err;

	hr = S_OK;
err:
	if ( pMonList != NULL )
		QUARTZ_CompList_Free( pMonList );
	RegCloseKey( hKey );

	return hr;
}

static ICOM_VTABLE(ICreateDevEnum) icreatedevenum =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	/* IUnknown fields */
	ICreateDevEnum_fnQueryInterface,
	ICreateDevEnum_fnAddRef,
	ICreateDevEnum_fnRelease,
	/* ICreateDevEnum fields */
	ICreateDevEnum_fnCreateClassEnumerator,
};

HRESULT CSysDevEnum_InitICreateDevEnum( CSysDevEnum* psde )
{
	TRACE("(%p)\n",psde);
	ICOM_VTBL(&psde->createdevenum) = &icreatedevenum;

	return NOERROR;
}

void CSysDevEnum_UninitICreateDevEnum( CSysDevEnum* psde )
{
	TRACE("(%p)\n",psde);
}


/***************************************************************************
 *
 *	CDeviceMoniker::IMoniker
 *
 */

static HRESULT WINAPI
IMoniker_fnQueryInterface(IMoniker* iface,REFIID riid,void** ppobj)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);

	return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
}

static ULONG WINAPI
IMoniker_fnAddRef(IMoniker* iface)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);

	return IUnknown_AddRef(This->unk.punkControl);
}

static ULONG WINAPI
IMoniker_fnRelease(IMoniker* iface)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);

	return IUnknown_Release(This->unk.punkControl);
}

static HRESULT WINAPI IMoniker_fnGetClassID(IMoniker* iface, CLSID *pClassID)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);

	if ( pClassID == NULL )
		return E_POINTER;
	memcpy( pClassID, &CLSID_CDeviceMoniker, sizeof(CLSID) );

	return NOERROR;
}

static HRESULT WINAPI IMoniker_fnIsDirty(IMoniker* iface)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnLoad(IMoniker* iface, IStream* pStm)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnSave(IMoniker* iface, IStream* pStm, BOOL fClearDirty)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnGetSizeMax(IMoniker* iface, ULARGE_INTEGER* pcbSize)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnBindToObject(IMoniker* iface,IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riid, VOID** ppvResult)
{
	CDeviceMoniker_THIS(iface,moniker);
	HRESULT	hr;
	IPropertyBag*	pPropBag;
	IPersistPropertyBag*	pPersistPropBag;
	VARIANT	vClsid;
	CLSID	clsid;

	TRACE("(%p)->(%p,%p,%s,%p)\n",This,
		pbc,pmkToLeft,debugstr_guid(riid),ppvResult);
	if ( pbc != NULL )
	{
		FIXME("IBindCtx* pbc != NULL not implemented.\n");
		return E_FAIL;
	}
	if ( pmkToLeft != NULL )
	{
		FIXME("IMoniker* pmkToLeft != NULL not implemented.\n");
		return E_FAIL;
	}
	if ( ppvResult == NULL )
		return E_POINTER;

	hr = QUARTZ_CreateRegPropertyBag(
			This->m_hkRoot, This->m_pwszPath, &pPropBag );
	if ( FAILED(hr) )
		return hr;

	vClsid.n1.n2.vt = VT_BSTR;
	hr = IPropertyBag_Read(
			pPropBag, QUARTZ_wszCLSID, &vClsid, NULL );
	IPropertyBag_Release( pPropBag );
	if ( FAILED(hr) )
		return hr;

	hr = CLSIDFromString( vClsid.n1.n2.n3.bstrVal, &clsid );
	SysFreeString(vClsid.n1.n2.n3.bstrVal);
	if ( FAILED(hr) )
		return hr;

	hr = CoCreateInstance(
		&clsid, NULL, CLSCTX_INPROC_SERVER, riid, ppvResult );
	if ( FAILED(hr) )
		return hr;

	hr = IUnknown_QueryInterface((IUnknown*)*ppvResult,&IID_IPersistPropertyBag,(void**)&pPersistPropBag);
	if ( hr == E_NOINTERFACE )
	{
		hr = S_OK;
	}
	else
	if ( SUCCEEDED(hr) )
	{
		hr = QUARTZ_CreateRegPropertyBag(
				This->m_hkRoot, This->m_pwszPath, &pPropBag );
		if ( SUCCEEDED(hr) )
		{
			hr = IPersistPropertyBag_Load(pPersistPropBag,pPropBag,NULL);
			IPropertyBag_Release( pPropBag );
		}
		IPersistPropertyBag_Release(pPersistPropBag);
	}

	if ( FAILED(hr) )
	{
		IUnknown_Release((IUnknown*)*ppvResult);
		*ppvResult = NULL;
	}

	TRACE( "hr = %08lx\n", hr );

	return hr;
}

static HRESULT WINAPI IMoniker_fnBindToStorage(IMoniker* iface,IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riid, VOID** ppvResult)
{
	CDeviceMoniker_THIS(iface,moniker);
	HRESULT	hr;

	TRACE("(%p)->(%p,%p,%s,%p)\n",This,
		pbc,pmkToLeft,debugstr_guid(riid),ppvResult);
	if ( pbc != NULL )
	{
		FIXME("IBindCtx* pbc != NULL not implemented.\n");
		return E_FAIL;
	}
	if ( pmkToLeft != NULL )
	{
		FIXME("IMoniker* pmkToLeft != NULL not implemented.\n");
		return E_FAIL;
	}
	if ( ppvResult == NULL )
		return E_POINTER;

	hr = E_NOINTERFACE;
	if ( IsEqualGUID(riid,&IID_IUnknown) ||
		 IsEqualGUID(riid,&IID_IPropertyBag) )
	{
		hr = QUARTZ_CreateRegPropertyBag(
			This->m_hkRoot, This->m_pwszPath,
			(IPropertyBag**)ppvResult );
	}

	return hr;
}

static HRESULT WINAPI IMoniker_fnReduce(IMoniker* iface,IBindCtx* pbc, DWORD dwReduceHowFar,IMoniker** ppmkToLeft, IMoniker** ppmkReduced)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);

	if ( ppmkReduced == NULL )
		return E_POINTER;

	*ppmkReduced = iface; IMoniker_AddRef(iface);

	return MK_S_REDUCED_TO_SELF;
}

static HRESULT WINAPI IMoniker_fnComposeWith(IMoniker* iface,IMoniker* pmkRight,BOOL fOnlyIfNotGeneric, IMoniker** ppmkComposite)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnEnum(IMoniker* iface,BOOL fForward, IEnumMoniker** ppenumMoniker)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);

	if ( ppenumMoniker == NULL )
		return E_POINTER;

	*ppenumMoniker = NULL;
	return NOERROR;
}

static HRESULT WINAPI IMoniker_fnIsEqual(IMoniker* iface,IMoniker* pmkOtherMoniker)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnHash(IMoniker* iface,DWORD* pdwHash)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnIsRunning(IMoniker* iface,IBindCtx* pbc, IMoniker* pmkToLeft, IMoniker* pmkNewlyRunning)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnGetTimeOfLastChange(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, FILETIME* pCompositeTime)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnInverse(IMoniker* iface,IMoniker** ppmk)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnCommonPrefixWith(IMoniker* iface,IMoniker* pmkOther, IMoniker** ppmkPrefix)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnRelativePathTo(IMoniker* iface,IMoniker* pmOther, IMoniker** ppmkRelPath)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnGetDisplayName(IMoniker* iface,IBindCtx* pbc, IMoniker* pmkToLeft, LPOLESTR *ppszDisplayName)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnParseDisplayName(IMoniker* iface,IBindCtx* pbc, IMoniker* pmkToLeft, LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut)
{
	CDeviceMoniker_THIS(iface,moniker);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI IMoniker_fnIsSystemMoniker(IMoniker* iface,DWORD* pdwMksys)
{
	CDeviceMoniker_THIS(iface,moniker);

	TRACE("(%p)->()\n",This);
	if ( pdwMksys == NULL )
		return E_POINTER;

	*pdwMksys = MKSYS_NONE;
	return S_FALSE;
}


static ICOM_VTABLE(IMoniker) imoniker =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	/* IUnknown fields */
	IMoniker_fnQueryInterface,
	IMoniker_fnAddRef,
	IMoniker_fnRelease,
	/* IPersist fields */
	IMoniker_fnGetClassID,
	/* IPersistStream fields */
	IMoniker_fnIsDirty,
	IMoniker_fnLoad,
	IMoniker_fnSave,
	IMoniker_fnGetSizeMax,
	/* IMoniker fields */
	IMoniker_fnBindToObject,
	IMoniker_fnBindToStorage,
	IMoniker_fnReduce,
	IMoniker_fnComposeWith,
	IMoniker_fnEnum,
	IMoniker_fnIsEqual,
	IMoniker_fnHash,
	IMoniker_fnIsRunning,
	IMoniker_fnGetTimeOfLastChange,
	IMoniker_fnInverse,
	IMoniker_fnCommonPrefixWith,
	IMoniker_fnRelativePathTo,
	IMoniker_fnGetDisplayName,
	IMoniker_fnParseDisplayName,
	IMoniker_fnIsSystemMoniker,
};


static HRESULT CDeviceMoniker_InitIMoniker(
	CDeviceMoniker* pdm, HKEY hkRoot, LPCWSTR lpKeyPath )
{
	DWORD	dwLen;

	ICOM_VTBL(&pdm->moniker) = &imoniker;
	pdm->m_hkRoot = hkRoot;
	pdm->m_pwszPath = NULL;

	dwLen = sizeof(WCHAR)*(lstrlenW(lpKeyPath)+1);
	pdm->m_pwszPath = (WCHAR*)QUARTZ_AllocMem( dwLen );
	if ( pdm->m_pwszPath == NULL )
		return E_OUTOFMEMORY;
	memcpy( pdm->m_pwszPath, lpKeyPath, dwLen );

	return NOERROR;
}

static void CDeviceMoniker_UninitIMoniker(
	CDeviceMoniker* pdm )
{
	if ( pdm->m_pwszPath != NULL )
		QUARTZ_FreeMem( pdm->m_pwszPath );
}

/***************************************************************************
 *
 *	new/delete for CDeviceMoniker
 *
 */

static void QUARTZ_DestroyDeviceMoniker(IUnknown* punk)
{
	CDeviceMoniker_THIS(punk,unk);

	CDeviceMoniker_UninitIMoniker( This );
}

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry CDeviceMoniker_IFEntries[] =
{
  { &IID_IPersist, offsetof(CDeviceMoniker,moniker)-offsetof(CDeviceMoniker,unk) },
  { &IID_IPersistStream, offsetof(CDeviceMoniker,moniker)-offsetof(CDeviceMoniker,unk) },
  { &IID_IMoniker, offsetof(CDeviceMoniker,moniker)-offsetof(CDeviceMoniker,unk) },
};

HRESULT QUARTZ_CreateDeviceMoniker(
	HKEY hkRoot, LPCWSTR lpKeyPath,
	IMoniker** ppMoniker )
{
	CDeviceMoniker*	pdm;
	HRESULT	hr;

	TRACE("(%08x,%s,%p)\n",hkRoot,debugstr_w(lpKeyPath),ppMoniker );

	pdm = (CDeviceMoniker*)QUARTZ_AllocObj( sizeof(CDeviceMoniker) );
	if ( pdm == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &pdm->unk, NULL );
	hr = CDeviceMoniker_InitIMoniker( pdm, hkRoot, lpKeyPath );
	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj( pdm );
		return hr;
	}

	pdm->unk.pEntries = CDeviceMoniker_IFEntries;
	pdm->unk.dwEntries = sizeof(CDeviceMoniker_IFEntries)/sizeof(CDeviceMoniker_IFEntries[0]);
	pdm->unk.pOnFinalRelease = &QUARTZ_DestroyDeviceMoniker;

	*ppMoniker = (IMoniker*)(&pdm->moniker);

	return S_OK;
}


/***************************************************************************
 *
 *	CRegPropertyBag::IPropertyBag
 *
 */

static HRESULT WINAPI
IPropertyBag_fnQueryInterface(IPropertyBag* iface,REFIID riid,void** ppobj)
{
	CRegPropertyBag_THIS(iface,propbag);

	TRACE("(%p)->()\n",This);

	return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
}

static ULONG WINAPI
IPropertyBag_fnAddRef(IPropertyBag* iface)
{
	CRegPropertyBag_THIS(iface,propbag);

	TRACE("(%p)->()\n",This);

	return IUnknown_AddRef(This->unk.punkControl);
}

static ULONG WINAPI
IPropertyBag_fnRelease(IPropertyBag* iface)
{
	CRegPropertyBag_THIS(iface,propbag);

	TRACE("(%p)->()\n",This);

	return IUnknown_Release(This->unk.punkControl);
}

static HRESULT WINAPI
IPropertyBag_fnRead(IPropertyBag* iface,LPCOLESTR lpszPropName,VARIANT* pVar,IErrorLog* pLog)
{
	CRegPropertyBag_THIS(iface,propbag);
	HRESULT hr;
	LONG	lr;
	DWORD	dwSize;
	DWORD	dwValueType;
	DWORD	dwDWordValue;
	SAFEARRAYBOUND	sab;
	SAFEARRAY*	pArray;

	TRACE("(%p)->(%s,%p,%p)\n",This,
		debugstr_w(lpszPropName),pVar,pLog);

	if ( lpszPropName == NULL || pVar == NULL )
		return E_POINTER;

	dwSize = 0;
	lr = RegQueryValueExW(
		This->m_hKey, lpszPropName, NULL,
		&dwValueType, NULL, &dwSize );
	if ( lr != ERROR_SUCCESS )
	{
		WARN( "RegQueryValueExW failed.\n" );
		return E_INVALIDARG;
	}

	switch ( dwValueType )
	{
	case REG_SZ:
		TRACE( "REG_SZ / length = %lu\n", dwSize );
		if ( pVar->n1.n2.vt == VT_EMPTY )
			pVar->n1.n2.vt = VT_BSTR;
		if ( pVar->n1.n2.vt != VT_BSTR )
		{
			FIXME( "type of VARIANT is not BSTR.\n" );
			return E_FAIL;
		}

		pVar->n1.n2.n3.bstrVal = SysAllocStringByteLen(
			NULL, dwSize );
		if ( pVar->n1.n2.n3.bstrVal == NULL )
		{
			WARN( "out of memory.\n" );
			return E_OUTOFMEMORY;
		}
		lr = RegQueryValueExW(
			This->m_hKey, lpszPropName, NULL,
			&dwValueType,
			(BYTE*)pVar->n1.n2.n3.bstrVal, &dwSize );
		if ( lr != ERROR_SUCCESS )
		{
			WARN( "failed to query value\n" );
			SysFreeString(pVar->n1.n2.n3.bstrVal);
			return E_FAIL;
		}
		TRACE( "value is BSTR; %s\n", debugstr_w(pVar->n1.n2.n3.bstrVal) );
		break;
	case REG_BINARY:
		TRACE( "REG_BINARY / length = %lu\n", dwSize );
		if ( pVar->n1.n2.vt == VT_EMPTY )
			pVar->n1.n2.vt = VT_ARRAY|VT_UI1;
		if ( pVar->n1.n2.vt != (VT_ARRAY|VT_UI1) )
		{
			FIXME( "type of VARIANT is not VT_ARRAY|VT_UI1.\n" );
			return E_FAIL;
		}
		sab.lLbound = 0;
		sab.cElements = dwSize;
		pArray = SafeArrayCreate( VT_UI1, 1, &sab );
		if ( pArray == NULL )
			return E_OUTOFMEMORY;
		hr = SafeArrayLock( pArray );
		if ( FAILED(hr) )
		{
			WARN( "safe array can't be locked\n" );
			SafeArrayDestroy( pArray );
			return hr;
		}
		lr = RegQueryValueExW(
			This->m_hKey, lpszPropName, NULL,
			&dwValueType,
			(BYTE*)pArray->pvData, &dwSize );
		SafeArrayUnlock( pArray );
		if ( lr != ERROR_SUCCESS )
		{
			WARN( "failed to query value\n" );
			SafeArrayDestroy( pArray );
			return E_FAIL;
		}
		pVar->n1.n2.n3.parray = pArray;
		TRACE( "value is SAFEARRAY - array of BYTE; \n" );
		break;
	case REG_DWORD:
		TRACE( "REG_DWORD / length = %lu\n", dwSize );
		if ( dwSize != sizeof(DWORD) )
		{
			WARN( "The length of REG_DWORD value is not sizeof(DWORD).\n" );
			return E_FAIL;
		}
		if ( pVar->n1.n2.vt == VT_EMPTY )
			pVar->n1.n2.vt = VT_I4;
		if ( pVar->n1.n2.vt != VT_I4 )
		{
			FIXME( "type of VARIANT is not VT_I4.\n" );
			return E_FAIL;
		}
		lr = RegQueryValueExW(
			This->m_hKey, lpszPropName, NULL,
			&dwValueType,
			(BYTE*)(&dwDWordValue), &dwSize );
		if ( lr != ERROR_SUCCESS )
		{
			WARN( "failed to query value\n" );
			return E_FAIL;
		}
		pVar->n1.n2.n3.lVal = dwDWordValue;
		TRACE( "value is DWORD; %08lx\n", dwDWordValue );
		break;
	default:
		FIXME("(%p)->(%s,%p,%p) - unsupported value type.\n",This,
			debugstr_w(lpszPropName),pVar,pLog);
		return E_FAIL;
	}

	TRACE( "returned successfully.\n" );
	return NOERROR;
}

static HRESULT WINAPI
IPropertyBag_fnWrite(IPropertyBag* iface,LPCOLESTR lpszPropName,VARIANT* pVar)
{
	CRegPropertyBag_THIS(iface,propbag);
	HRESULT hr;
	LONG	lr;
	DWORD	dwDWordValue;
	SAFEARRAY*	pArray;

	TRACE("(%p)->(%s,%p)\n",This,
		debugstr_w(lpszPropName),pVar);

	if ( lpszPropName == NULL || pVar == NULL )
		return E_POINTER;

	switch ( pVar->n1.n2.vt )
	{
	case VT_I4: /* REG_DWORD */
		dwDWordValue = pVar->n1.n2.n3.lVal;
		lr = RegSetValueExW(
			This->m_hKey, lpszPropName, 0,
			REG_DWORD,
			(const BYTE*)(&dwDWordValue), sizeof(DWORD) );
		if ( lr != ERROR_SUCCESS )
		{
			WARN( "failed to set value\n" );
			return E_FAIL;
		}
		break;
	case VT_BSTR: /* REG_SZ */
		lr = RegSetValueExW(
			This->m_hKey, lpszPropName, 0,
			REG_SZ,
			(const BYTE*)(pVar->n1.n2.n3.bstrVal),
			SysStringByteLen( pVar->n1.n2.n3.bstrVal ) );
		if ( lr != ERROR_SUCCESS )
		{
			WARN( "failed to set value\n" );
			return E_FAIL;
		}
		break;
	case (VT_ARRAY|VT_UI1): /* REG_BINARY */
		pArray = pVar->n1.n2.n3.parray;
		if ( pArray->cDims != 1 || pArray->cbElements != 1 ||
			 pArray->rgsabound[0].lLbound != 0 )
		{
			WARN( "invalid array\n" );
			return E_INVALIDARG;
		}
		hr = SafeArrayLock( pArray );
		if ( FAILED(hr) )
		{
			WARN( "safe array can't be locked\n" );
			return hr;
		}
		lr = RegSetValueExW(
			This->m_hKey, lpszPropName, 0,
			REG_BINARY,
			(const BYTE*)pArray->pvData,
			pArray->rgsabound[0].cElements );
		SafeArrayUnlock( pArray );
		if ( lr != ERROR_SUCCESS )
		{
			WARN( "failed to set value\n" );
			return E_FAIL;
		}
		break;
	default:
		FIXME("(%p)->(%s,%p) invalid/unsupported VARIANT type %04x\n",This,
			debugstr_w(lpszPropName),pVar,pVar->n1.n2.vt);
		return E_INVALIDARG;
	}

	TRACE( "returned successfully.\n" );
	return NOERROR;
}




static ICOM_VTABLE(IPropertyBag) ipropbag =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	/* IUnknown fields */
	IPropertyBag_fnQueryInterface,
	IPropertyBag_fnAddRef,
	IPropertyBag_fnRelease,
	/* IPropertyBag fields */
	IPropertyBag_fnRead,
	IPropertyBag_fnWrite,
};

static HRESULT CRegPropertyBag_InitIPropertyBag(
	CRegPropertyBag* prpb, HKEY hkRoot, LPCWSTR lpKeyPath )
{
	WCHAR	wszREG_SZ[ NUMELEMS(QUARTZ_wszREG_SZ) ];
	DWORD	dwDisp;

	ICOM_VTBL(&prpb->propbag) = &ipropbag;

	memcpy(wszREG_SZ,QUARTZ_wszREG_SZ,sizeof(QUARTZ_wszREG_SZ) );

	if ( RegCreateKeyExW(
			hkRoot, lpKeyPath, 0, wszREG_SZ,
			REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
			&prpb->m_hKey, &dwDisp ) != ERROR_SUCCESS )
		return E_FAIL;

	return NOERROR;
}

static void CRegPropertyBag_UninitIPropertyBag(
	CRegPropertyBag* prpb )
{
	RegCloseKey( prpb->m_hKey );
}


/***************************************************************************
 *
 *	new/delete for CRegPropertyBag
 *
 */

static void QUARTZ_DestroyRegPropertyBag(IUnknown* punk)
{
	CRegPropertyBag_THIS(punk,unk);

	CRegPropertyBag_UninitIPropertyBag(This);
}


/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry CRegPropertyBag_IFEntries[] =
{
  { &IID_IPropertyBag, offsetof(CRegPropertyBag,propbag)-offsetof(CRegPropertyBag,unk) },
};

HRESULT QUARTZ_CreateRegPropertyBag(
	HKEY hkRoot, LPCWSTR lpKeyPath,
	IPropertyBag** ppPropBag )
{
	CRegPropertyBag*	prpb;
	HRESULT	hr;

	TRACE("(%08x,%s,%p)\n",hkRoot,debugstr_w(lpKeyPath),ppPropBag );

	prpb = (CRegPropertyBag*)QUARTZ_AllocObj( sizeof(CRegPropertyBag) );
	if ( prpb == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &prpb->unk, NULL );
	hr = CRegPropertyBag_InitIPropertyBag( prpb, hkRoot, lpKeyPath );
	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj( prpb );
		return hr;
	}

	prpb->unk.pEntries = CRegPropertyBag_IFEntries;
	prpb->unk.dwEntries = sizeof(CRegPropertyBag_IFEntries)/sizeof(CRegPropertyBag_IFEntries[0]);
	prpb->unk.pOnFinalRelease = &QUARTZ_DestroyRegPropertyBag;

	*ppPropBag = (IPropertyBag*)(&prpb->propbag);

	return S_OK;
}

/***************************************************************************
 *
 *	Helper for registering filters.
 *
 */

HRESULT QUARTZ_GetFilterRegPath(
	WCHAR** ppwszPath,	/* [OUT] path from HKEY_CLASSES_ROOT */
	const CLSID* pguidFilterCategory,	/* [IN] Category */
	const CLSID* pclsid,	/* [IN] CLSID of this filter */
	LPCWSTR lpInstance )	/* [IN] instance */
{
	HRESULT	hr;
	WCHAR	szKey[ 1024 ];
	WCHAR	szFilterPath[ 512 ];
	WCHAR	szCLSID[ 256 ];
	int	buflen;

	TRACE("(%p,%s,%s,%s)\n",ppwszPath,debugstr_guid(pguidFilterCategory),debugstr_guid(pclsid),debugstr_w(lpInstance) );

	*ppwszPath = NULL;

	QUARTZ_GUIDtoString( szCLSID, pclsid );
	lstrcpyW( szFilterPath, QUARTZ_wszInstance );
	QUARTZ_CatPathSepW( szFilterPath );
	if ( lpInstance != NULL )
	{
		if ( lstrlenW(lpInstance) >= 256 )
			return E_INVALIDARG;
		lstrcatW( szFilterPath, lpInstance );
	}
	else
	{
		lstrcatW( szFilterPath, szCLSID );
	}

	hr = QUARTZ_CreateCLSIDPath(
		szKey, NUMELEMS(szKey),
		pguidFilterCategory, szFilterPath );
	if ( FAILED(hr) )
		return hr;

	buflen = sizeof(WCHAR)*(lstrlenW(szKey)+1);
	*ppwszPath = QUARTZ_AllocMem( buflen );
	if ( *ppwszPath == NULL )
		return E_OUTOFMEMORY;
	memcpy( *ppwszPath, szKey, buflen );
	return S_OK;
}

HRESULT QUARTZ_RegisterFilterToMoniker(
	IMoniker* pMoniker,	/* [IN] Moniker */
	const CLSID* pclsid,	/* [IN] CLSID of this filter */
	LPCWSTR lpFriendlyName,	/* [IN] friendly name */
	const BYTE* pbFilterData,	/* [IN] filter data */
	DWORD cbFilterData )	/* [IN] size of the filter data */
{
	IPropertyBag*	pPropBag = NULL;
	WCHAR	wszClsid[128];
	VARIANT var;
	HRESULT hr;
	SAFEARRAYBOUND	sab;
	SAFEARRAY*	pArray = NULL;

	TRACE("(%p,%s,%s,%p,%08lu)\n",pMoniker,debugstr_guid(pclsid),debugstr_w(lpFriendlyName),pbFilterData,cbFilterData);

	hr = IMoniker_BindToStorage(pMoniker,NULL,NULL,&IID_IPropertyBag,(void**)&pPropBag);
	if ( FAILED(hr) )
		goto err;
	QUARTZ_GUIDtoString( wszClsid, pclsid );
	var.n1.n2.vt = VT_BSTR;
	var.n1.n2.n3.bstrVal = SysAllocString(wszClsid);
	if ( var.n1.n2.n3.bstrVal == NULL )
	{
		hr = E_OUTOFMEMORY;
		goto err;
	}
	hr = IPropertyBag_Write(pPropBag,QUARTZ_wszCLSID,&var);
	SysFreeString(var.n1.n2.n3.bstrVal);
	if ( FAILED(hr) )
		goto err;

	var.n1.n2.vt = VT_BSTR;
	var.n1.n2.n3.bstrVal = SysAllocString(lpFriendlyName);
	if ( var.n1.n2.n3.bstrVal == NULL )
	{
		hr = E_OUTOFMEMORY;
		goto err;
	}
	hr = IPropertyBag_Write(pPropBag,QUARTZ_wszFriendlyName,&var);
	SysFreeString(var.n1.n2.n3.bstrVal);
	if ( FAILED(hr) )
		goto err;

	if ( pbFilterData != NULL )
	{
		var.n1.n2.vt = VT_ARRAY|VT_UI1;
		sab.lLbound = 0;
		sab.cElements = cbFilterData;
		pArray = SafeArrayCreate( VT_UI1, 1, &sab );
		if ( pArray == NULL )
		{
			hr = E_OUTOFMEMORY;
			goto err;
		}
		hr = SafeArrayLock( pArray );
		if ( FAILED(hr) )
			goto err;
		var.n1.n2.n3.parray = pArray;
		memcpy( pArray->pvData, pbFilterData, cbFilterData );
		hr = IPropertyBag_Write(pPropBag,QUARTZ_wszFilterData,&var);
		SafeArrayUnlock( pArray );
		if ( FAILED(hr) )
			goto err;
	}

	hr = NOERROR;
err:
	if ( pArray != NULL )
		SafeArrayDestroy( pArray );
	if ( pPropBag != NULL )
		IPropertyBag_Release(pPropBag);

	return hr;
}

HRESULT QUARTZ_RegDeleteKey( HKEY hkRoot, LPCWSTR lpKeyPath )
{
	LONG	lr;
	HRESULT hr;
	HKEY	hKey;
	DWORD	dwIndex;
	DWORD	cbName;
	FILETIME	ftLastWrite;
	DWORD	dwType;
	WCHAR	wszPath[ 512 ];

	TRACE("(%08x,%s)\n",hkRoot,debugstr_w(lpKeyPath));

	lr = RegOpenKeyExW( hkRoot, lpKeyPath, 0, KEY_ALL_ACCESS, &hKey );
	if ( lr == ERROR_SUCCESS )
	{
		dwIndex = 0;
		while ( 1 )
		{
			cbName = NUMELEMS(wszPath);
			lr = RegEnumKeyExW(
				hKey, dwIndex, wszPath, &cbName,
				NULL, NULL, NULL, &ftLastWrite );
			if ( lr != ERROR_SUCCESS )
				break;
			hr = QUARTZ_RegDeleteKey( hKey, wszPath );
			if ( FAILED(hr) )
				return hr;
			if ( hr != S_OK )
				dwIndex ++;
		}
		while ( 1 )
		{
			cbName = NUMELEMS(wszPath);
			lr = RegEnumValueW(
				hKey, 0, wszPath, &cbName, 0,
				&dwType, NULL, 0 );
			if ( lr != ERROR_SUCCESS )
				break;
			lr = RegDeleteValueW( hKey, wszPath );
			if ( lr != ERROR_SUCCESS )
			{
				WARN("RegDeleteValueW - %08lx\n",lr);
				return E_FAIL;
			}
		}
	}
	RegCloseKey( hKey );

	lr = RegDeleteKeyW( hkRoot, lpKeyPath );
	WARN("RegDeleteKeyW - %08lx\n",lr);
	if ( lr != ERROR_SUCCESS && lr != ERROR_FILE_NOT_FOUND )
		return S_FALSE;
	return S_OK;
}

static
HRESULT QUARTZ_GetPropertyFromMoniker(
	IMoniker* pMoniker,	/* [IN] Moniker */
	LPCOLESTR lpszPropName,	/* [IN] Property */
	VARIANT* pVar )	/* [OUT] */
{
	IPropertyBag*	pPropBag = NULL;
	HRESULT hr;

	TRACE("(%p,%s,%p)\n",pMoniker,debugstr_w(lpszPropName),pVar);

	hr = IMoniker_BindToStorage(pMoniker,NULL,NULL,&IID_IPropertyBag,(void**)&pPropBag);
	if ( FAILED(hr) )
		return hr;
	hr = IPropertyBag_Read(pPropBag,lpszPropName,pVar,NULL);
	IPropertyBag_Release(pPropBag);

	return hr;
}

HRESULT QUARTZ_GetCLSIDFromMoniker(
	IMoniker* pMoniker,	/* [IN] Moniker */
	CLSID* pclsid )	/* [OUT] */
{
	VARIANT	var;
	HRESULT hr;

	var.n1.n2.vt = VT_BSTR;
	hr = QUARTZ_GetPropertyFromMoniker(
		pMoniker, QUARTZ_wszCLSID, &var );
	if ( hr == S_OK )
	{
		hr = CLSIDFromString(var.n1.n2.n3.bstrVal,pclsid);
		SysFreeString(var.n1.n2.n3.bstrVal);
	}

	return hr;
}

HRESULT QUARTZ_GetMeritFromMoniker(
	IMoniker* pMoniker,	/* [IN] Moniker */
	DWORD* pdwMerit )	/* [OUT] */
{
	VARIANT	var;
	HRESULT hr;

	var.n1.n2.vt = VT_I4;
	hr = QUARTZ_GetPropertyFromMoniker(
		pMoniker, QUARTZ_wszMerit, &var );
	if ( hr == S_OK )
		*pdwMerit = var.n1.n2.n3.lVal;

	return hr;
}

HRESULT QUARTZ_GetFilterDataFromMoniker(
	IMoniker* pMoniker,	/* [IN] Moniker */
	BYTE** ppbFilterData,	/* [OUT] */
	DWORD* pcbFilterData )	/* [OUT] */
{
	VARIANT	var;
	HRESULT hr;
	SAFEARRAY*	pArray;

	var.n1.n2.vt = VT_ARRAY|VT_UI1;
	hr = QUARTZ_GetPropertyFromMoniker(
		pMoniker, QUARTZ_wszFilterData, &var );
	if ( hr == S_OK )
	{
		pArray = var.n1.n2.n3.parray;
		hr = SafeArrayLock( pArray );
		if ( SUCCEEDED(hr) )
		{
			*pcbFilterData = pArray->rgsabound[0].cElements - pArray->rgsabound[0].lLbound;
			*ppbFilterData = (BYTE*)QUARTZ_AllocMem( *pcbFilterData );
			if ( *ppbFilterData == NULL )
				hr = E_OUTOFMEMORY;
			else
				memcpy( *ppbFilterData, pArray->pvData, *pcbFilterData );

			SafeArrayUnlock( pArray );
		}
		SafeArrayDestroy( pArray );
	}

	return hr;
}

