/*!
 *  \file Registry04.cpp
 *  \brief レジストリ操作クラス CRegistry04 のインプリメンテーション
 *  \author kishy
 *
 *  \since 2002/6/8
 */

#include "stdafx.h"
#include "Registry04.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CRegistry04

CRegistry04::CRegistry04()
: m_fAttached(FALSE), m_hKey(0) , m_hParentKey(0), m_mode(0), m_disposition(0)
, m_pathName(), m_nSubKeys(0), m_nMaxSubKeyNameLen(0), m_nEnumIndex(0)
, m_nValues(0), m_nMaxValueNameLen(0), m_nMaxValueDataLen(0), m_subKeyName()
, m_bMessage(FALSE)
{
}


/////////////////////////////////////////////////////////////////////////////
// CRegistry04 オペレーション

BOOL CRegistry04::Open(HKEY hKey, const CString &subKeyName, const CString &mode)
{
	BOOL retCode = FALSE;

	enum ERR_CODE
	{
		ERR_SUBKEYNAME_NOT_SPECIFIED,
		ERR_ACCESSMODE_NOT_SPECIFIED,
		ERR_INVALID_ACCESSMODE,
		ERR_CANT_OPEN_KEY,
		ERR_CANT_QUERYINFO_KEY
	};

	try
	{
		// 引数のチェック
		if(subKeyName == "") throw ERR_SUBKEYNAME_NOT_SPECIFIED;
		if(mode == "") throw ERR_ACCESSMODE_NOT_SPECIFIED;

		// 開いているレジストリがあれば閉じる
		if(m_fAttached == FALSE)
			Close();
		else
			Detach();

		// モードの判定
		switch(mode[0])
		{
			case _T('r'):
			case _T('R'):
				m_mode = KEY_READ;
			break;

			case _T('w'):
			case _T('W'):
				m_mode = KEY_WRITE | DELETE;
			break;

			case _T('a'):
			case _T('A'):
				m_mode = KEY_ALL_ACCESS;
			break;

			default:
			throw ERR_INVALID_ACCESSMODE;
		}

		// 作成モードの指定
		if(mode.GetLength() > 1)
			switch(mode[1])
			{
				case _T('+'):
					m_mode |= KEY_CREATE_SUB_KEY | KEY_CREATE_LINK;
				break;

				case _T('-'):
					m_mode &= ~(KEY_CREATE_SUB_KEY | KEY_CREATE_LINK);
				break;

				case _T('\0'):
				break;

				default:
				throw ERR_INVALID_ACCESSMODE;
			}

		// レジストリキーを開く
		if(m_mode & KEY_CREATE_SUB_KEY)
		{
			// キーを開く、キーが無ければ新規作成
			if(
				RegCreateKeyEx(
					hKey,						// handle of an open key
					subKeyName,					// address of subkey name 
					0,							// reserved 
					NULL,						// address of class string
					REG_OPTION_NON_VOLATILE,	// special options flag 
					m_mode,						// desired security access
					NULL,						// address of key security structure 
					&m_hKey,					// address of buffer for opened handle
					&m_disposition				// address of disposition value buffer 
				) != ERROR_SUCCESS
			){
				throw ERR_CANT_OPEN_KEY;
			}
 
		}
		else{
			// キーを開く、キーが無ければエラー
			if(
				RegOpenKeyEx(
					hKey,		// handle of open key
					subKeyName,	// address of name of subkey to open
					0,			// reserved
					m_mode,		// security access mask
					&m_hKey		// address of handle of open key
				) != ERROR_SUCCESS
			){
				throw ERR_CANT_OPEN_KEY;
			}

			m_disposition = REG_OPENED_EXISTING_KEY;
		}

		m_hParentKey = hKey;
		m_subKeyName = HKEY2String(hKey) + subKeyName;

		// キーの詳細情報を得る
		if(QueryInfoKey())
			throw ERR_CANT_QUERYINFO_KEY;
	}
	catch(ERR_CODE err)
	{
		CString msg;
		switch(err)
		{
			case ERR_SUBKEYNAME_NOT_SPECIFIED:
				msg = _T("サブキー名が指定されていません。");
			break;
			case ERR_ACCESSMODE_NOT_SPECIFIED:
				msg = _T("アクセスモードが指定されていません。");
			break;
			case ERR_INVALID_ACCESSMODE:
				msg = _T("アクセスモードが不正です。");
			break;
			case ERR_CANT_OPEN_KEY:
				msg = _T("レジストリキーを開けません。");
			break;
			case ERR_CANT_QUERYINFO_KEY:
				msg = _T("レジストリキーの情報を取得できません。");
			break;

		}

		MessageBox(msg);

		Close();
		retCode = TRUE;
	}

	return retCode;
}

BOOL CRegistry04::Open(const CRegistry04 &reg, const CString &subKeyName, const CString &mode)
{
	BOOL retCode = Open(reg.m_hKey, subKeyName, mode);
	m_subKeyName = reg.m_subKeyName + "\\" + m_subKeyName;
	return retCode;
}

void CRegistry04::Close() throw()
{
	if((m_hKey != 0) && (m_fAttached == FALSE))
	{
		::RegCloseKey(m_hKey);
		Clean();
	}
}

inline BOOL CRegistry04::Refresh()
{
	return QueryInfoKey();
}

BOOL CRegistry04::Attach(HKEY hKey, const CString &keyName, HKEY hParentKey)
{
	m_fAttached = TRUE;

	m_hKey = hKey;
	m_subKeyName = HKEY2String(hKey) + keyName;
	m_hParentKey = hParentKey;

	return QueryInfoKey();
}

HKEY CRegistry04::Detach() throw()
{
	HKEY hKey = m_hKey;

	if(m_fAttached)
		Clean();
	else
		return 0;

	return hKey;
}

DWORD CRegistry04::EnumSubKey(BOOL fInitialize)
{
	DWORD nSubKeyNameLength = m_nMaxSubKeyNameLen + 1;

	if(fInitialize)
		m_nEnumIndex = 0;

	if(
		RegEnumKeyEx(
			m_hKey,				// handle of key to enumerate
			m_nEnumIndex,		// index of subkey to enumerate
			m_subKeyName.GetBuffer(nSubKeyNameLength),
								// address of buffer for subkey name
			&nSubKeyNameLength,	// address for size of subkey buffer
			NULL,	// reserved
			NULL,	// address of buffer for class string
			NULL,	// address for size of class buffer 
			NULL	// address for time key last written to 
		) != ERROR_SUCCESS
	){
		return 0;
	}

	m_subKeyName.ReleaseBuffer();
	m_nEnumIndex ++;

	return nSubKeyNameLength;
}

BOOL CRegistry04::DeleteSubKey(const CString &subKeyName)
{
	CRegistry04 subkey;

	// サブキー・値情報の更新
	if(QueryInfoKey())
		return TRUE;

	// 指定サブキーのオープン
	if(subkey.Open(m_hKey, subKeyName, "a"))
		return TRUE;

	// 指定サブキーが下位サブキーを持っているか？
	if(subkey.GetSubKeys() != 0)
	{
		// 指定サブキーの下位を再帰削除
		if(subkey.EnumSubKey(TRUE) != 0)
		{
			while(1)
			{
				// 見付かったサブキーを削除
				subkey.DeleteSubKey(subkey.GetSubKeyName());

				if(subkey.EnumSubKey() != 0)
					break;
			}
		}
	}

	// 指定サブキーをクローズ
	subkey.Close();

	// 指定サブキーを削除
	RegDeleteKey(m_hKey, subKeyName);

	// サブキー・値情報の更新
	QueryInfoKey();

	return FALSE;
}

BOOL CRegistry04::DeleteValue(const CString &valueName)
{
	BOOL retCode = FALSE;

	// 値を削除
	if(
		RegDeleteValue(
			m_hKey,				// handle of key to query 
			valueName			// address of name of value to query 
		)
	){
		retCode = TRUE;
	}

	return retCode;
}

BOOL CRegistry04::QueryValue(LPBYTE &pValue, DWORD &nValueLen, const CString &valueName)
{
	BOOL retCode = FALSE;

	DWORD type = REG_BINARY;

	// 値を検索
	if(
		RegQueryValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			&type,				// address of buffer for value type
			pValue,				// address of data buffer 
			&nValueLen			// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}

BOOL CRegistry04::QueryValue(DWORD &value, const CString &valueName)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = sizeof(DWORD);
	DWORD type = REG_DWORD;

	// 値を検索
	if(
		RegQueryValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			&type,				// address of buffer for value type
			(LPBYTE)&value,		// address of data buffer 
			&nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}

#if defined(REG_QWORD)
BOOL CRegistry04::QueryValue(QWORD &value, const CString &valueName)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = sizeof(QWORD);
	DWORD type = REG_QWORD;

	// 値を検索
	if(
		RegQueryValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			&type,				// address of buffer for value type
			(LPBYTE)&value,		// address of data buffer 
			&nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}
#endif

BOOL CRegistry04::QueryValue(CString &value, const CString &valueName)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = m_nMaxValueDataLen;
	DWORD type = REG_SZ;

	// 値を検索
	if(
		RegQueryValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			&type,				// address of buffer for value type
			(LPBYTE)value.GetBuffer(nValueDataLen),
								// address of data buffer 
			&nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	value.ReleaseBuffer();

	return retCode;
}

BOOL CRegistry04::QueryValue(CStringArray &value, const CString &valueName)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = m_nMaxValueDataLen;
	DWORD type = REG_MULTI_SZ;
	LPBYTE buf = new BYTE[nValueDataLen];

	// 値を検索
	if(
		RegQueryValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			&type,				// address of buffer for value type
			buf,				// address of data buffer 
			&nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}
	else
	{
		LPTSTR pHead = (LPTSTR)buf, pTail = (LPTSTR)buf;
		int index = 0;

		value.RemoveAll();

		while(1)
		{
			while(pTail < (LPTSTR)(buf + nValueDataLen))
			{
				if(*pTail ++ == _T('\0'))
					break;
			}

			value.SetAtGrow(index, pHead);

			pHead = ++ pTail;
			if((pHead >= (LPTSTR)(buf + nValueDataLen)) || (*pHead == _T('\0')))
				break;
		}
	}

	delete[] buf;

	return retCode;
}

BOOL CRegistry04::SetValue(const CString &valueName, LPBYTE pValue, DWORD nValueLen)
{
	BOOL retCode = FALSE;

	DWORD type = REG_BINARY;

	// 値を設定
	if(
		RegSetValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			type,				// address of buffer for value type
			pValue,				// address of data buffer 
			nValueLen			// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}

BOOL CRegistry04::SetValue(const CString &valueName, DWORD value)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = sizeof(DWORD);
	DWORD type = REG_DWORD;

	// 値を設定
	if(
		RegSetValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			type,				// address of buffer for value type
			(LPBYTE)&value,		// address of data buffer 
			nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}

#if defined(REG_QWORD)
BOOL CRegistry04::SetValue(const CString &valueName, QWORD value)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = sizeof(QWORD);
	DWORD type = REG_QWORD;

	// 値を設定
	if(
		RegSetValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			type,				// address of buffer for value type
			(LPBYTE)&value,		// address of data buffer 
			nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}
#endif

BOOL CRegistry04::SetValue(const CString &valueName, const CString &value)
{
	BOOL retCode = FALSE;

	DWORD nValueDataLen = (lstrlen(value) + 1) * sizeof(TCHAR);
	DWORD type = REG_SZ;

	// 値を設定
	if(
		RegSetValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			type,				// address of buffer for value type
			(LPBYTE const)((LPCTSTR)value),
								// address of data buffer 
			nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	return retCode;
}

BOOL CRegistry04::SetValue(const CString &valueName, const CStringArray &value)
{
	int i, strings;

	DWORD nValueDataLen = 0;
	DWORD type = REG_MULTI_SZ;

	BOOL retCode = FALSE;

	// 文字列の数を得る
	strings = (int)value.GetSize();
	if(strings == 0) return FALSE;

	// 合計文字数を得る
	for(i = 0; i < strings; i++)
		nValueDataLen += value[i].GetLength();

	// 合計バイト数を算出
	nValueDataLen = (nValueDataLen + strings + 1) * sizeof(TCHAR);

	// バッファを確保
	LPBYTE buf = new BYTE[nValueDataLen];

	// 格納イメージを作成
	LPTSTR pBuf = (LPTSTR)buf;
	LPCTSTR pStr;

	for(i = 0; i < strings; i++)
	{
		pStr = value[i];
		while(*pStr != _T('\0'))
			*pBuf ++ = *pStr ++;
		*pBuf ++ = _T('\0');
	}

	*pBuf ++ = _T('\0');

	// 値を設定
	if(
		RegSetValueEx(
			m_hKey,				// handle of key to query 
			valueName,			// address of name of value to query 
			NULL,				// reserved
			type,				// address of buffer for value type
			buf,				// address of data buffer 
			nValueDataLen		// address of data buffer size 
 		)  != ERROR_SUCCESS
	){
		retCode = TRUE;
	}

	delete[] buf;

	return retCode;
}

BOOL CRegistry04::QuerySubKeyValue(const CString &subKeyName, LPBYTE &pValue, DWORD &nValueLen, const CString &valueName)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.QueryValue(pValue, nValueLen, valueName);
}

BOOL CRegistry04::QuerySubKeyValue(const CString &subKeyName, DWORD &value, const CString &valueName)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.QueryValue(value, valueName);
}

BOOL CRegistry04::QuerySubKeyValue(const CString &subKeyName, CString &value, const CString &valueName)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.QueryValue(value, valueName);
}

BOOL CRegistry04::QuerySubKeyValue(const CString &subKeyName, CStringArray &value, const CString &valueName)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.QueryValue(value, valueName);
}

BOOL CRegistry04::SetSubKeyValue(const CString &subKeyName, const CString &valueName, LPBYTE pValue, DWORD nValueLen)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.SetValue(valueName, pValue, nValueLen);
}

BOOL CRegistry04::SetSubKeyValue(const CString &subKeyName, const CString &valueName, DWORD value)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.SetValue(valueName, value);
}

#if defined(REG_QWORD)
BOOL CRegistry04::SetSubKeyValue(const CString &subKeyName, const CString &valueName, QWORD value)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.SetValue(valueName, value);
}
#endif

BOOL CRegistry04::SetSubKeyValue(const CString &subKeyName, const CString &valueName, const CString &value)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.SetValue(valueName, value);
}

BOOL CRegistry04::SetSubKeyValue(const CString &subKeyName, const CString &valueName, const CStringArray &value)
{
	CRegistry04 subkey;

	if(subkey.Open(*this, subKeyName, "w"))
		return TRUE;

	return subkey.SetValue(valueName, value);
}

/////////////////////////////////////////////////////////////////////////////
// CRegistry04 インプリメンテーション

CRegistry04::~CRegistry04() throw()
{
	// レジストリを閉じる
	Close();
}

void CRegistry04::Clean() throw()
{
	m_fAttached = FALSE;

	m_hKey = 0;
	m_hParentKey = 0;
	m_mode = 0;
	m_disposition = 0;

	m_pathName.Empty();

	m_nSubKeys = 0;
	m_nMaxSubKeyNameLen = 0;
	m_nValues = 0;
	m_nMaxValueNameLen = 0;
	m_nMaxValueDataLen = 0;
	m_nEnumIndex = 0;

	m_subKeyName.Empty();
}



BOOL CRegistry04::QueryInfoKey()
{
	// キーの詳細情報を得る
	if(
		RegQueryInfoKey(
			m_hKey,					// handle of key to query
			NULL,					// address of buffer for class string
			NULL,					// address of size of class string buffer
			NULL,					// reserved
			&m_nSubKeys,			// address of buffer for number of subkeys
			&m_nMaxSubKeyNameLen,	// address of buffer for longest subkey name length
			NULL,					// address of buffer for longest class string length
			&m_nValues,				// address of buffer for number of value entries
			&m_nMaxValueNameLen,	// address of buffer for longest value name length
			&m_nMaxValueDataLen,	// address of buffer for longest value data length
			NULL,					// address of buffer for security descriptor length 
			NULL					// address of buffer for last write time 
		) != ERROR_SUCCESS
	){
		return TRUE;
	}

	return FALSE;
}


void CRegistry04::MessageBox(const CString &msg)
{
	if(m_bMessage) AfxMessageBox(_T("CRegistry04 : ") + msg);
}

CString CRegistry04::HKEY2String(HKEY hKey)
{
	switch((__int64)hKey)
	{
		case (__int64)HKEY_CLASSES_ROOT:
		return _T("\\HKEY_CLASSES_ROOT");
		case (__int64)HKEY_CURRENT_CONFIG:
		return _T("\\HKEY_CURRENT_CONFIG");
		case (__int64)HKEY_CURRENT_USER:
		return _T("\\HKEY_CURRENT_USER");
		case (__int64)HKEY_LOCAL_MACHINE:
		return _T("\\HKEY_LOCAL_MACHINE");
		case (__int64)HKEY_USERS:
		return _T("\\HKEY_USERS");
		default:
		return "";
	}
}


