/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbODregistry
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbODregistry.h"
#include <winreg.h>
//#include <shlobj.h>
#include "../../xbException.h"
//#include "../../xbDnItf.h"

#define xbCPCONVERT_UTF8TOLERANT

xbODregEngine	g_xbODregEngine("xdcBase Registry Engine");

//xbODregPath	xbODregXdcMachine("XdcMachine","xdcBase Base Machine Configuration Path",xbODregLocalMachine,"Software\\MMM xdCube");
//xbODregPath	xbODregXdcUser("XdcUser","xdcBase Base User Configuration Path",xbODregCurrentUser,"Software\\MMM xdCube");

class xbODregKeyInfo
{
public:
	xbODregKeyInfo(HKEY hKey, bool bRdOnly, const xbODregRoot mk,const xbString &name)
	{
		m_cref = 1;
		m_pParent = NULL;
		m_mainkey = mk;
		m_name = name;
		m_hkey = hKey;
		m_bRdOnly = bRdOnly;
	}
	xbODregKeyInfo(const xbODregRoot mk,const xbString &name)
	{
		m_cref = 1;
		m_pParent = NULL;
		m_mainkey = mk;
		m_name = name;
		m_hkey = NULL;
		m_bRdOnly = false;
	}
	xbODregKeyInfo(xbODregKeyInfo &Parent,const xbString &name)
	{
		m_cref = 1;
		m_name = name;
		if (m_name.IsEmpty()) xbThrowEarg("xbODregKeyInfo::xbODregKeyInfo(Parent,path) : path is empty");
		m_mainkey = Parent.m_mainkey;
		m_hkey = NULL;
		m_bRdOnly = false;
		m_pParent = &Parent;
		m_pParent->AddRef();
	}
	LONG AddRef()
	{
		LONG cref = xbInterlockIncrement32(&m_cref) ;
		return cref;
	}

	LONG Release()
	{
		LONG cref;
		if ((cref=xbInterlockDecrement32(&m_cref)) == 0)
		{
			m_cref = 1;
			delete this ;
			return 0 ;
		}
		return cref ;
	}
	enum AccessType
	{
		AccessRead,
			AccessCreate,
			AccessDel
	};

	HKEY GetHKey(AccessType accessType)
	{
		if (m_hkey) 
		{
			if (accessType != AccessRead && m_bRdOnly)
				xbThrowEsys(ERROR_ACCESS_DENIED,"xbODregKeyInfo::GetHKey() : write access denied");
			return m_hkey;
		}
		long lResult;
		HKEY	mainkey;
		xbString keypath = m_name;
		bool	bParentRdOnly;
		if (m_pParent) 
		{
			mainkey = m_pParent->GetHKey(accessType);
			if (!mainkey) 
			{
				xbAssert(accessType != AccessCreate);
				return NULL;
			}
			bParentRdOnly = m_pParent->m_bRdOnly;
		}
		else
		{
			bParentRdOnly = false;
			xbODregRoot	rootKey(m_mainkey);
			if (int(rootKey) < 0)
			{
				g_xbODregEngine.GetPath(rootKey,&rootKey,&keypath);
				if (!keypath.IsEmpty()) 
				{
					if (!m_name.IsEmpty()) keypath += "\\"+m_name;
				}
				else keypath = m_name;
			}
			switch(rootKey)
			{
			case xbODregCurrentUser :
				mainkey = HKEY_CURRENT_USER;
				break;
#if(WINVER >= 0x0400)
#ifndef _PDA
			case xbODregCurrentConfig :
				mainkey = HKEY_CURRENT_CONFIG;
				break;
#endif
#endif
			case xbODregClassesRoot :
				mainkey = HKEY_CLASSES_ROOT;
				break;
			case xbODregUsers :
				mainkey = HKEY_USERS;
				break;
			case xbODregLocalMachine :
				mainkey = HKEY_LOCAL_MACHINE;
				break;
#ifndef _PDA
#if(WINVER >= 0x0400)
			case xbODregDynData :
				mainkey = HKEY_DYN_DATA;
				break;
#endif
			case xbODregPerformance :
				mainkey = HKEY_PERFORMANCE_DATA;
				break;
#endif
			default:
				xbThrowEsys("xbODregKeyInfo::GetHKey() : bad root key ("+xbString((int)rootKey)+")");
			}
		}
		if (accessType == AccessCreate)
		{
			xbAssert(!bParentRdOnly);
			lResult = RegCreateKeyEx(mainkey ,
	                              keypath, 
	                              0, NULL, REG_OPTION_NON_VOLATILE,
	                              KEY_ALL_ACCESS, NULL, 
	                              &m_hkey, NULL) ;
			m_bRdOnly = false;
		}
		else
		{
			if (bParentRdOnly)
			{
				lResult = ERROR_ACCESS_DENIED;
				xbAssert(accessType == AccessRead);
			}
			else
			{
				lResult = RegOpenKeyEx(mainkey ,
	                              keypath, 
	                              0,
	                              KEY_ALL_ACCESS,
	                              &m_hkey);
				m_bRdOnly = false;
			}
			if (accessType == AccessRead
				&& HRESULT_CODE(lResult) == ERROR_ACCESS_DENIED)
			{
				lResult = RegOpenKeyEx(mainkey ,
	                              keypath, 
	                              0,
	                              KEY_ENUMERATE_SUB_KEYS|KEY_EXECUTE|KEY_QUERY_VALUE,
	                              &m_hkey);
				m_bRdOnly = true;
			}
		}
		if (lResult != ERROR_SUCCESS)
		{
			m_hkey = NULL;
			if (accessType == AccessCreate) xbThrowEsys(lResult,"xbODregKeyInfo::GetHKey() : Creation of key '"+GetPath()+"' failed !");
			else if(HRESULT_CODE(lResult) != ERROR_FILE_NOT_FOUND)
				 xbThrowEsys(lResult,"xbODregKeyInfo::GetHKey() : Opening key '"+GetPath()+"' failure !");
			return NULL;
		}
		return m_hkey;
	}
	xbODregRoot	GetRoot()
	{
		return m_mainkey;
	}
	xbString		GetName()
	{
		return m_name;
	}
	xbString		GetPath()
	{
		if (!m_pParent) return m_name;
		return m_pParent->GetPath() + "\\" + m_name;
	}
	void Close()
	{
		if (m_hkey == NULL) return;
		RegCloseKey(m_hkey);
		m_hkey = NULL;
	}
	bool GetValue(const char *valuename,xbVariant *pValue)
	{
		HKEY hkey = GetHKey(AccessRead);
		if (!hkey)
		{
			if (pValue) pValue->Empty();
			return false;
		}

		DWORD keysize=0;
		DWORD keytype = REG_SZ;
		LONG lSuccess = ::RegQueryValueEx( hkey, valuename, NULL, &keytype, NULL, &keysize );
		if( lSuccess!=ERROR_FILE_NOT_FOUND && lSuccess!=ERROR_SUCCESS )
			xbThrowEsys(lSuccess,"xbODregKeyInfo::GetValue("+xbString(valuename)+") : RegQueryValueEx getting size and type failure (path='"+GetPath()+"') !");
		if (lSuccess==ERROR_SUCCESS && pValue)
		{
			xbData	val;
			lSuccess = ::RegQueryValueEx( hkey, valuename, NULL, &keytype, (LPBYTE)val.GetWritePtr(keysize), &keysize );
			if( lSuccess!=ERROR_FILE_NOT_FOUND && lSuccess!=ERROR_SUCCESS )
				xbThrowEsys(lSuccess,"xbODregKeyInfo::GetValue("+xbString(valuename)+") : RegQueryValueEx getting value failure (size="+xbString(keysize)+", path='"+GetPath()+"') !");
			if (lSuccess==ERROR_SUCCESS)
			{
				pValue->Lcid = xbLCID_US;
				switch(keytype)
				{
				case REG_SZ :
				case REG_EXPAND_SZ :
					*pValue = (const char*)((const void*)val);
					break;
				case REG_DWORD :
					*pValue = *(const long*)((const void*)val);
					break;
				case REG_QWORD :
					*pValue = *(const int64*)((const void*)val);
					break;
				case REG_BINARY :
					pValue->SetData(val);
					break;
				default:
					xbThrowEsys(E_NOTIMPL,"xbODregKeyInfo::GetValue("+xbString(valuename)+") : The value exists but its type is actually not supported (path='"+GetPath()+"') !");
				}
			}
		}
		if (lSuccess!=ERROR_SUCCESS) 
		{
			if (pValue) pValue->Empty();
			return false;
		}
		return true;
	}
	void SetValue(const char *valuename,const xbVariant &value,bool bAltFrmt)
	{
		HKEY hkey = GetHKey(AccessCreate);
		int32		valnb;
		int64		valnb64;
		xbString	valstr;
		xbData		data;
		DWORD		keysize;
		DWORD		keytype;
		const BYTE  *keyptr;
		if (value.IsData())
		{
			keytype = REG_BINARY;
			value.GetData(&data);
			keysize = data.GetLength();
			keyptr = (const BYTE *)((const void*)data);
		}
		else
		{
			switch(value.GetType())
			{
			case VT_EMPTY :
				xbThrowEarg("xbODregKeyInfo::SetValue("+xbString(valuename)+",value) : Invalid argument, variant 'value' is empty (path='"+GetPath()+"') !");
			case VT_BOOL : keytype = REG_DWORD; keysize = sizeof(valnb); valnb = value.AsBool?1:0; keyptr = (BYTE*)&valnb; break;
			case VT_I1 : 
			case VT_UI1 : keytype = REG_DWORD; keysize = sizeof(valnb); valnb = value.AsUchar; keyptr = (BYTE*)&valnb; break;
			case VT_UI2 : 
			case VT_I2 : keytype = REG_DWORD; keysize = sizeof(valnb); valnb = value.AsShort; keyptr = (BYTE*)&valnb; break;
			case VT_UI4 : 
			case VT_I4 : keytype = REG_DWORD; keysize = sizeof(valnb); valnb = value.AsLong; keyptr = (BYTE*)&valnb; break;
			case VT_I8 : keytype = REG_QWORD; keysize = sizeof(valnb64); valnb64 = value.AsInt64; keyptr = (BYTE*)&valnb64; break;
			default:
				valstr = value.GetAsString(xbLCID_US);
				keytype = bAltFrmt?REG_EXPAND_SZ:REG_SZ;
				keyptr = (BYTE*)((LPCSTR)valstr);
				keysize = valstr.GetLength() + 1; 
			}
		}
		LONG lSuccess = RegSetValueEx(hkey, valuename, 0, keytype,keyptr,keysize);
		if( lSuccess!=ERROR_SUCCESS )
			xbThrowEsys(lSuccess,"xbODregKeyInfo::SetValue("+xbString(valuename)+",'"+value.AsString+"') : RegSetValueEx setting value failure (path='"+GetPath()+"') !");
	}
	bool Exists() {return GetHKey(AccessRead) != NULL?true:false;}
	bool DeleteValue(const char *valuename,xbOpMode opMode)
	{
		HKEY hkey = GetHKey(AccessDel);
		if( !hkey )
		{
			if (opMode == xbOp_Required) 
				xbThrowEsys(ERROR_FILE_NOT_FOUND,"xbODregKeyInfo::DeleteValue("+xbString(valuename)+") : Key does not exist (path='"+GetPath()+"') !");
			return false;
		}
		LONG lSuccess = ::RegDeleteValue(hkey,valuename);
		if( lSuccess==ERROR_SUCCESS ) 
		{
			return true;
		}
		if (lSuccess == ERROR_FILE_NOT_FOUND)
		{
			if (opMode == xbOp_Required) 
				xbThrowEsys(ERROR_FILE_NOT_FOUND,"xbODregKeyInfo::DeleteValue("+xbString(valuename)+") : Value does not exist (path='"+GetPath()+"') !");
			return false;
		}
		xbThrowEsys(lSuccess,"xbODregKeyInfo::DeleteValue("+xbString(valuename)+") : RegDeleteValue failure (path='"+GetPath()+"') !");
		return false;
	}

	bool DeleteKey(bool bRecurse,xbOpMode opMode)
	{
		if (!Exists())
		{
			if (opMode == xbOp_Required) 
				xbThrowEsys(ERROR_FILE_NOT_FOUND,"xbODregKeyInfo::DeleteKey() : Key does not exist (path='"+GetPath()+"') !");
			return false;
		}
		if (GetPath().GetLength() < 1)
			xbThrowEsys(E_ACCESSDENIED,"xbODregKeyInfo::DeleteKey() : Can't remove root keys (path='"+GetPath()+"') !");

		{
			xbODregEnumKey keyenum(this);
			xbODregKey	subkey;
			while(keyenum.Next(&subkey))
			{
				if (!bRecurse)
					xbThrowEsys(MAKE_HRESULT(SEVERITY_ERROR,FACILITY_STORAGE,ERROR_DIR_NOT_EMPTY),"xbODregKeyInfo::DeleteKey() : This key contains sub keys (path='"+GetPath()+"') !");
				subkey.DeleteKey(true,xbOp_Required);
				keyenum.Reset();
			}
		}
		Close();
		HKEY hkey = NULL;
		xbString path = m_name;
		if (m_pParent)
		{
			hkey = m_pParent->GetHKey(AccessDel);
		}
		else
		{
			xbODregRoot	rootKey(m_mainkey);
			if (int(rootKey) < 0)
			{
				g_xbODregEngine.GetPath(rootKey,&rootKey,&path);
				if (!path.IsEmpty()) 
				{
					if (!m_name.IsEmpty()) path += "\\"+m_name;
				}
				else path = m_name;
			}
			switch(rootKey)
			{
			case xbODregCurrentUser :
				hkey = HKEY_CURRENT_USER;
				break;
#if(WINVER >= 0x0400)
#ifndef _PDA
			case xbODregCurrentConfig :
				hkey = HKEY_CURRENT_CONFIG;
				break;
#endif
#endif
			case xbODregClassesRoot :
				hkey = HKEY_CLASSES_ROOT;
				break;
			case xbODregUsers :
				hkey = HKEY_USERS;
				break;
			case xbODregLocalMachine :
				hkey = HKEY_LOCAL_MACHINE;
				break;
#ifndef _PDA
#if(WINVER >= 0x0400)
			case xbODregDynData :
				hkey = HKEY_DYN_DATA;
				break;
#endif
			case xbODregPerformance :
				hkey = HKEY_PERFORMANCE_DATA;
				break;
#endif
			default:
				xbThrowEarg("xbODregKeyInfo::DeleteKey() : bad root key ("+xbString(int(rootKey))+")");
			}
		}
		if (!hkey) 
			xbThrowEsys(ERROR_FILE_NOT_FOUND,"xbODregKeyInfo::DeleteKey() : Can't open parent key (parent key path'"+m_pParent->GetPath()+"', key path='"+GetPath()+"') !");
		if (path.IsEmpty()) 
			xbThrowEsys(ERROR_FILE_NOT_FOUND,"xbODregKeyInfo::DeleteKey() : Can't open parent key (parent key path'"+m_pParent->GetPath()+"', key path='"+GetPath()+"') !");
		LONG lSuccess = ::RegDeleteKey(hkey,path);
		if (lSuccess != ERROR_SUCCESS) 
			xbThrowEsys(lSuccess,"xbODregKeyInfo::DeleteKey() : RegDeleteValue failure (path='"+GetPath()+"') !");
		
		return true;
	}

	bool EnumKey(DWORD pos,xbString *pName)
	{
		HKEY hkey = GetHKey(AccessRead);
		if( !hkey ) return false;
		CHAR buf[256];
		LONG lSuccess = ::RegEnumKey(hkey,pos,buf,sizeof(buf));
		if( lSuccess==ERROR_SUCCESS ) 
		{
			if (pName) *pName = buf; 
			return true;
		}
		if( lSuccess==ERROR_NO_MORE_ITEMS ) return false;
		xbThrowEsys(lSuccess,"xbODregKeyInfo::EnumKey("+xbString(pos)+") : RegEnumKey failure (path='"+GetPath()+"') !");
		return false;
	}

	bool EnumValue(DWORD pos,xbString *pName)
	{
		HKEY hkey = GetHKey(AccessRead);
		if( !hkey ) return false;
		CHAR	buf[256];
		DWORD	szbuf = sizeof(buf);
		LONG lSuccess = ::RegEnumValue(hkey,pos,buf,&szbuf,NULL,NULL,NULL,NULL);
		if( lSuccess==ERROR_SUCCESS ) 
		{
			if (pName) *pName = buf; 
			return true;
		}
		if( lSuccess==ERROR_NO_MORE_ITEMS ) return false;
		xbThrowEsys(lSuccess,"xbODregKeyInfo::EnumValue("+xbString(pos)+") : RegEnumValue failure (path='"+GetPath()+"') !");
		return false;
	}
	bool EnumValue(DWORD pos,xbString *pName,xbVariant *pValue)
	{
		HKEY hkey = GetHKey(AccessRead);
		if( !hkey ) return false;
		CHAR	buf[256];
		DWORD	szbuf = sizeof(buf);
		DWORD	keytype;
		DWORD	keysz = 0;
		LONG	lSuccess = ::RegEnumValue(hkey,pos,buf,&szbuf,NULL,&keytype,NULL,&keysz);
		if (lSuccess==ERROR_SUCCESS)
		{
			if (!pValue)
			{
				if (pName) *pName = buf; 
				return true;
			}
			szbuf = sizeof(buf);
			xbData	val;
			lSuccess = ::RegEnumValue( hkey, pos, buf, &szbuf, NULL, &keytype, (LPBYTE)val.GetWritePtr(keysz), &keysz );
			if( lSuccess!=ERROR_FILE_NOT_FOUND && lSuccess!=ERROR_SUCCESS )
				xbThrowEsys(lSuccess,"xbODregKeyInfo::EnumValue("+xbString(pos)+": '"+xbString(buf)+"') : RegEnumValue getting value failure (size="+xbString(keysz)+", path='"+GetPath()+"') !");
			if (lSuccess==ERROR_SUCCESS)
			{
				switch(keytype)
				{
				case REG_SZ :
					*pValue = (const char*)((const void*)val);
					break;
				case REG_EXPAND_SZ :
					*pValue = (const char*)((const void*)val);
					break;
				case REG_DWORD :
					*pValue = *(const long*)((const void*)val);
					break;
				case REG_QWORD :
					*pValue = *(const int64*)((const void*)val);
					break;
				case REG_BINARY :
					pValue->SetData(val);
					break;
				default:
					xbThrowEsys(E_NOTIMPL,"xbODregKeyInfo::EnumValue("+xbString(pos)+": '"+xbString(buf)+"') : The value exists but its type is actually not supported (size="+xbString(keysz)+", path='"+GetPath()+"') !");
				}
				if (pName) *pName = buf; 
				return true;
			}
		}
		if( lSuccess==ERROR_NO_MORE_ITEMS ) return false;
		xbThrowEsys(lSuccess,"xbODregKeyInfo::EnumValue("+xbString(pos)+") : RegEnumValue failure (path='"+GetPath()+"') !");
		return false;
	}

	~xbODregKeyInfo()
	{
		eProtectBegin();
		Close();
		if (m_pParent) m_pParent->Release();
		xbAssert(m_cref == 1);
		eProtectEndTrace();
	}
	xbHeapNewImpl(xbODregKeyInfo);
private:
	xbODregKeyInfo	*m_pParent;
	xbODregRoot		m_mainkey;
	xbString		m_name;
	HKEY			m_hkey;
	LONG			m_cref;
	bool			m_bRdOnly;
};

xbODregKey::xbODregKey(const xbODregRoot mk,const xbString &keyname)
{
	m_pInfo = xbDnew xbODregKeyInfo(mk,keyname);
}

xbODregKey::xbODregKey(xbODregKey &parent,const xbString &keyname)
{
	m_pInfo = xbDnew xbODregKeyInfo(*parent.m_pInfo,keyname);
}

xbODregKey::xbODregKey(xbODregKey &key)
{
	key.m_pInfo->AddRef();
	m_pInfo = key.m_pInfo;
}

void xbODregKey::Init(const xbODregRoot mk,const xbString &keyname)
{
	Close();
	m_pInfo = xbDnew xbODregKeyInfo(mk,keyname);
}

void xbODregKey::Init(xbODregKey &parent,const xbString &keyname)
{
	Close();
	m_pInfo = xbDnew xbODregKeyInfo(*parent.m_pInfo,keyname);
}

void xbODregKey::Init(xbODregKey &key)
{
	Close();
	key.m_pInfo->AddRef();
	m_pInfo = key.m_pInfo;
}

void xbODregKey::Init(xbODregKeyInfo *pInfo,const xbString &keyname)
{
	Close();
	m_pInfo = xbDnew xbODregKeyInfo(*pInfo,keyname);
}

xbODregKey::xbODregKey()
{
	m_pInfo = NULL;
}

xbODregKey &xbODregKey::operator=(const xbODregKey &key)
{
	key.m_pInfo->AddRef();
	if (m_pInfo) m_pInfo->Release();
	m_pInfo = key.m_pInfo;
	return *this;
}

void	xbODregKey::Close()
{
	if (!m_pInfo) return;
	xbODregKeyInfo *pInfo = m_pInfo;
	m_pInfo = NULL;
	pInfo->Release();
}

xbODregKey::~xbODregKey()
{
	eProtect(Close());
}

xbVariant xbODregKey::GetDefaultValue() const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::GetDefaultValue() : not linked to a registry key !");
	xbVariant val;
	m_pInfo->GetValue(NULL,&val);
	return val;
}

void	 xbODregKey::SetDefaultValue(const xbVariant &val,bool bAltFrmt)
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::SetDefaultValue() : not linked to a registry key !");
	m_pInfo->SetValue(NULL,val,bAltFrmt);
}

bool	 xbODregKey::DefaultValueExists() const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::IsDefaultValueDefined() : not linked to a registry key !");
	return m_pInfo->GetValue(NULL,NULL);
}

xbVariant xbODregKey::GetValue(const char *valname) const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::GetValue("+xbString(valname)+") : not linked to a registry key !");
	xbVariant val;
	m_pInfo->GetValue(valname,&val);	
	return val;
}

void	 xbODregKey::SetValue(const char *valname,const xbVariant &val,bool bAltFrmt)
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::SetValue("+xbString(valname)+",...) : not linked to a registry key !");
	m_pInfo->SetValue(valname,val,bAltFrmt);	
}

bool	 xbODregKey::ValueExists(const char *valname) const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::ValueExists() : not linked to a registry key !");
	return m_pInfo->GetValue(valname,NULL);
}

bool	 xbODregKey::Exists() const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::Exists() : not linked to a registry key !");
	return m_pInfo->Exists();
}

xbString xbODregKey::GetName() const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::GetName() : not linked to a registry key !");
	return m_pInfo->GetName();
}

xbString xbODregKey::GetPath() const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::GetPath() : not linked to a registry key !");
	return m_pInfo->GetPath();
}

xbODregRoot	xbODregKey::GetRoot() const
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::GetRootKey() : not linked to a registry key !");
	return m_pInfo->GetRoot();
}

bool	xbODregKey::DeleteValue(const char *valname,xbOpMode opMode)
{
	if (!m_pInfo) 
		xbThrowEsys("xbODregKey::DeleteValue() : not linked to a registry key !");
	return m_pInfo->DeleteValue(valname,opMode);
}

bool	xbODregKey::DeleteDefaultValue(xbOpMode opMode)
{
	if (!m_pInfo) 
		xbThrowEsys("xbODregKey::DeleteDefaultValue() : not linked to a registry key !");
	return m_pInfo->DeleteValue(NULL,opMode);
}

bool	xbODregKey::DeleteKey(bool bRecurse,xbOpMode opMode)
{
	if (!m_pInfo) 
		xbThrowEsys("xbODregKey::DeleteKey() : not linked to a registry key !");
	return m_pInfo->DeleteKey(bRecurse,opMode);
}

xbODregKey	&xbODregKey::CreateKey()
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::CreateKey() : not linked to a registry key !");
	m_pInfo->GetHKey(xbODregKeyInfo::AccessCreate);
	return *this;
}

bool xbODregKey::DeleteKey(const char *keyname,bool bRecurse,xbOpMode opMode)
{
	return xbODregKey(*this,keyname).DeleteKey(bRecurse,opMode);
}

HKEY xbODregKey::getHKey(bool bCreate)
{
	if (!m_pInfo) xbThrowEsys("xbODregKey::CreateKey() : not linked to a registry key !");
	return m_pInfo->GetHKey(bCreate?xbODregKeyInfo::AccessCreate:xbODregKeyInfo::AccessRead);
}

xbODregEnumKey::xbODregEnumKey(xbODregKey &key)
{
	m_pInfo = NULL;
	Init(key);
}

xbODregEnumKey::xbODregEnumKey(xbODregKeyInfo *pInfo)
{
	(m_pInfo=pInfo)->AddRef();
	m_pos = 0;
}

void xbODregEnumKey::Init(xbODregKey &key)
{
	if (!key.m_pInfo) xbThrowEarg("xbODregEnumKey::RegEnumKeyInit(xbODregKey &key) : argument key not linked to a registry key !");
	if (m_pInfo) m_pInfo->Release();
	key.m_pInfo->AddRef();
	m_pInfo = key.m_pInfo;
	m_pos = 0;
}

bool	xbODregEnumKey::Next(xbString *pKeyName)
{
	if (!pKeyName) xbThrowEsys("xbODregEnumKey::Next(pKeyName) : pKeyName is NULL !");
	if (!m_pInfo) xbThrowEsys("xbODregEnumKey::Next() : not linked to a registry key !");
	bool ret = m_pInfo->EnumKey(m_pos,pKeyName);
	if (ret) m_pos++;
	return ret;
}

bool	xbODregEnumKey::Next(xbODregKey *pSubKey)
{
	if (!pSubKey) xbThrowEsys("xbODregEnumKey::Next(pSubKey) : pSubKey is NULL !");
	if (!m_pInfo) xbThrowEsys("xbODregEnumKey::Next() : not linked to a registry key !");
	xbString name;
	bool ret = m_pInfo->EnumKey(m_pos,&name);
	if (ret)
	{
		pSubKey->Init(m_pInfo,name);
		m_pos++;
	}
	return ret;
}

bool	xbODregEnumKey::Next()
{
	if (!m_pInfo) xbThrowEsys("xbODregEnumKey::Next() : not linked to a registry key !");
	bool ret = m_pInfo->EnumKey(m_pos,NULL);
	if (ret) m_pos++;
	return ret;
}

void	xbODregEnumKey::Reset()
{
	m_pos = 0;
}

void	xbODregEnumKey::Close()
{
	if (!m_pInfo) return;
	xbODregKeyInfo *pInfo = m_pInfo;
	m_pInfo = NULL;
	pInfo->Release();
}

xbODregEnumKey::~xbODregEnumKey()
{
	eProtect(Close());
}

xbODregEnumValue::xbODregEnumValue(xbODregKey &key)
{
	m_pInfo = NULL;
	Init(key);
}

void xbODregEnumValue::Init(xbODregKey &key)
{
	if (!key.m_pInfo) xbThrowEarg("xbODregEnumValue::RegEnumValueInit(xbODregKey &key) : argument key not linked to a registry key !");
	if (m_pInfo) m_pInfo->Release();
	key.m_pInfo->AddRef();
	m_pInfo = key.m_pInfo;
	m_pos = 0;
}

bool	xbODregEnumValue::Next(xbString *pValName)
{
	if (!pValName) xbThrowEsys("xbODregEnumKey::Next(pValName) : pValName is NULL !");
	if (!m_pInfo) xbThrowEsys("xbODregEnumValue::Next() : not linked to a registry key !");
	bool ret = m_pInfo->EnumValue(m_pos,pValName);
	if (ret) m_pos++;
	return ret;
}

bool	xbODregEnumValue::Next(xbString *pValName,xbVariant *pValue)
{
	if (!m_pInfo) xbThrowEsys("xbODregEnumValue::Next() : not linked to a registry key !");
	bool ret = m_pInfo->EnumValue(m_pos,pValName,pValue);
	if (ret) m_pos++;
	return ret;
}

bool	xbODregEnumValue::Next()
{
	if (!m_pInfo) xbThrowEsys("xbODregEnumValue::Next() : not linked to a registry key !");
	bool ret = m_pInfo->EnumValue(m_pos,NULL);
	if (ret) m_pos++;
	return ret;
}

void	xbODregEnumValue::Reset()
{
	m_pos = 0;
}

void	xbODregEnumValue::Close()
{
	if (!m_pInfo) return;
	xbODregKeyInfo *pInfo = m_pInfo;
	m_pInfo = NULL;
	pInfo->Release();
}

xbODregEnumValue::~xbODregEnumValue()
{
	eProtect(Close());
}

void xbODregPath::GetAbsPath(xbODregRoot *pRoot,xbString *pPath) const
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	xbString	path;
	xbODregRoot	root;
	if (m_pRegPath)
	{
		m_pRegPath->GetAbsPath(&root,&path);
		if (!path.IsEmpty()) 
		{
			if (!m_path.IsEmpty()) path += "\\"+m_path;
		}
		else path = m_path;
	}
	else
	{
		path = m_path;
		root = m_root;
	}
	if (pRoot) *pRoot = root;
	if (pPath) *pPath = path;
}

xbString xbODregPath::GetPath() const
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	return m_path;
}

void xbODregPath::SetPath(const xbString &path)
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	m_path = path;
}

xbODregRoot	xbODregPath::GetRoot() const
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	if (m_pRegPath) return *m_pRegPath;
	return m_root;
}

void xbODregPath::SetRoot(xbODregRoot root)
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	if (int(root) < 0)
		m_pRegPath = g_xbODregEngine.GetRegPath(root);
	else
		m_pRegPath = NULL;
	m_root = root;
}

void xbODregPath::SetRoot(xbODregPath *pRegPathRoot)
{
	CheckReady();
	xbSectLock lck(&m_sectData);
	m_pRegPath = pRegPathRoot;
	m_root = xbODregCurrentUser;
}

void xbODregEngine::GetPath(xbODregRoot root,xbODregRoot *pRoot,xbString *pStr) const
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);
	int id = -int(root);
	if (id <= 0 || id > m_arrPath.Length
		|| !m_arrPath.unsafeGetAt(id-1))
		xbThrowEsys("xbODregEngine::GetPath(root="+xbString(id)+") : bad registry root id");

	m_arrPath.unsafeGetAt(id-1)->GetAbsPath(pRoot,pStr);
}

xbODregPath *xbODregEngine::GetRegPath(xbODregRoot root)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);
	int id = -int(root);
	if (id <= 0 || id > m_arrPath.Length
		|| !m_arrPath.unsafeGetAt(id-1))
		xbThrowEsys("xbODregEngine::GetRegPath(root="+xbString(id)+") : bad registry root id");
	return m_arrPath.unsafeGetAt(id-1);
}

int xbODregEngine::Add(xbODregPath *pRegPath)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);

	int no = 0;
	while(no < m_arrPath.Length)
	{
		if (!m_arrPath.unsafeGetAt(no))
		{
			m_arrPath.unsafeSetAt(no,pRegPath);
			return no+1;
		}
		no++;
	}
	return m_arrPath.Add(pRegPath)+1;
}

void xbODregEngine::Remove(int id)
{
	xbLock lckStart(this);
	xbSectLock lck(&m_sectData);
	m_arrPath.SetAt(id-1,NULL);
}

