mirror of
https://github.com/reactos/reactos.git
synced 2024-08-23 11:52:51 +00:00
642 lines
16 KiB
C++
642 lines
16 KiB
C++
/* $Id$
|
|
*
|
|
* regexpl - Console Registry Explorer
|
|
*
|
|
* Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
// RegistryTree.cpp: implementation of the CRegistryTree class.
|
|
|
|
#include "ph.h"
|
|
#include "RegistryTree.h"
|
|
#include "Pattern.h"
|
|
#include "RegistryExplorer.h"
|
|
|
|
CRegistryTree::CRegistryTree()
|
|
{
|
|
m_pszMachineName = NULL;
|
|
VERIFY(SUCCEEDED(m_Root.m_Key.InitRoot()));
|
|
m_Root.m_pUp = NULL;
|
|
m_pCurrentKey = &m_Root;
|
|
ASSERT(m_pCurrentKey->m_Key.IsRoot());
|
|
m_ErrorMsg[ERROR_MSG_BUFFER_SIZE] = 0;
|
|
}
|
|
|
|
CRegistryTree::CRegistryTree(const CRegistryTree& Tree)
|
|
{
|
|
m_pszMachineName = NULL;
|
|
VERIFY(SUCCEEDED(m_Root.m_Key.InitRoot()));
|
|
m_Root.m_pUp = NULL;
|
|
m_pCurrentKey = &m_Root;
|
|
ASSERT(m_pCurrentKey->m_Key.IsRoot());
|
|
|
|
const TCHAR *pszPath = Tree.GetCurrentPath();
|
|
if ((pszPath[0] == _T('\\')) && (pszPath[1] == _T('\\')))
|
|
{ // path has machine name
|
|
pszPath += 2;
|
|
while (*pszPath && (*pszPath != _T('\\')))
|
|
pszPath++;
|
|
|
|
ASSERT(*pszPath == _T('\\')); // if path begins with \\ it must be followed by machine name
|
|
}
|
|
|
|
if (Tree.m_pszMachineName)
|
|
SetMachineName(Tree.m_pszMachineName);
|
|
|
|
VERIFY(ChangeCurrentKey(pszPath));
|
|
}
|
|
|
|
CRegistryTree::~CRegistryTree()
|
|
{
|
|
if (m_pszMachineName)
|
|
delete m_pszMachineName;
|
|
|
|
CNode *pNode;
|
|
while(m_pCurrentKey->m_pUp)
|
|
{
|
|
pNode = m_pCurrentKey;
|
|
m_pCurrentKey = m_pCurrentKey->m_pUp;
|
|
delete pNode;
|
|
}
|
|
|
|
// We are on root
|
|
ASSERT(m_pCurrentKey->m_Key.IsRoot());
|
|
ASSERT(m_pCurrentKey == &m_Root);
|
|
}
|
|
|
|
const TCHAR * CRegistryTree::GetCurrentPath() const
|
|
{
|
|
return m_pCurrentKey->m_Key.GetKeyName();
|
|
}
|
|
|
|
BOOL CRegistryTree::IsCurrentRoot()
|
|
{
|
|
return m_pCurrentKey->m_Key.IsRoot();
|
|
}
|
|
|
|
BOOL CRegistryTree::ChangeCurrentKey(const TCHAR *pszRelativePath)
|
|
{
|
|
if (*pszRelativePath == _T('\\'))
|
|
GotoRoot(); // This is full absolute path.
|
|
|
|
// split path to key names.
|
|
const TCHAR *pszSeps = _T("\\");
|
|
|
|
// Make buffer and copy relative path into it.
|
|
TCHAR *pszBuffer = new TCHAR[_tcslen(pszRelativePath)+1];
|
|
if (!pszBuffer)
|
|
{
|
|
SetError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
_tcscpy(pszBuffer,pszRelativePath);
|
|
|
|
// We accept names in form "\"blablabla\\blab labla\"\\"
|
|
size_t size = _tcslen(pszBuffer);
|
|
if (size)
|
|
{
|
|
if (pszBuffer[size-1] == _T('\\'))
|
|
pszBuffer[--size] = 0;
|
|
|
|
TCHAR *psz;
|
|
if (*pszBuffer == _T('\"') && (psz = _tcschr(pszBuffer+1,_T('\"'))) && size_t(psz-pszBuffer) == size-1)
|
|
{
|
|
size--;
|
|
pszBuffer[size] = 0;
|
|
|
|
pszBuffer++;
|
|
}
|
|
}
|
|
|
|
const TCHAR *pszNewKey = _tcstok(pszBuffer,pszSeps);
|
|
|
|
if ((!pszNewKey)&&((*pszRelativePath != _T('\\'))||(*(pszRelativePath+1) != 0)))
|
|
{
|
|
SetError(_T("Invalid key name"));
|
|
goto Abort;
|
|
};
|
|
|
|
// change keys
|
|
while (pszNewKey)
|
|
{
|
|
if (!InternalChangeCurrentKey(pszNewKey,KEY_READ))
|
|
goto Abort; // InternalChangeCurrentKey sets last error description
|
|
|
|
// Get next key name
|
|
pszNewKey = _tcstok(NULL,pszSeps);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
Abort:
|
|
delete pszBuffer;
|
|
return FALSE;
|
|
}
|
|
|
|
const TCHAR * CRegistryTree::GetLastErrorDescription()
|
|
{
|
|
return m_ErrorMsg;
|
|
}
|
|
|
|
void CRegistryTree::GotoRoot()
|
|
{
|
|
// Delete current tree
|
|
CNode *pNode;
|
|
while(m_pCurrentKey->m_pUp)
|
|
{
|
|
pNode = m_pCurrentKey;
|
|
m_pCurrentKey = m_pCurrentKey->m_pUp;
|
|
delete pNode;
|
|
}
|
|
|
|
// We are on root
|
|
ASSERT(m_pCurrentKey->m_Key.IsRoot());
|
|
ASSERT(m_pCurrentKey == &m_Root);
|
|
}
|
|
|
|
BOOL CRegistryTree::SetMachineName(LPCTSTR pszMachineName)
|
|
{
|
|
GotoRoot();
|
|
|
|
// If we are going to local machine...
|
|
if (pszMachineName == NULL)
|
|
{
|
|
// Delete previous machine name buffer if allocated.
|
|
if (m_pszMachineName)
|
|
delete m_pszMachineName;
|
|
|
|
m_pszMachineName = NULL;
|
|
m_Root.m_Key.InitRoot();
|
|
return TRUE;
|
|
}
|
|
|
|
// Skip leading backslashes if any.
|
|
while ((*pszMachineName)&&(*pszMachineName == _T('\\')))
|
|
pszMachineName++;
|
|
|
|
ASSERT(*pszMachineName); // No machine name.
|
|
|
|
TCHAR *pszNewMachineName = new TCHAR[_tcslen(pszMachineName)+3]; // two leading backslashes + terminating null
|
|
|
|
if (!pszMachineName)
|
|
{
|
|
SetError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
// Delete previous machine name buffer if allocated.
|
|
if (m_pszMachineName)
|
|
delete m_pszMachineName;
|
|
|
|
m_pszMachineName = pszNewMachineName;
|
|
|
|
_tcscpy(m_pszMachineName,_T("\\\\")); // leading backslashes
|
|
_tcscpy(m_pszMachineName+2,pszMachineName); // machine name itself
|
|
_tcsupr(m_pszMachineName+2); // upercase it
|
|
|
|
VERIFY(SUCCEEDED(m_Root.m_Key.InitRoot(m_pszMachineName)));
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CRegistryTree::NewKey(const TCHAR *pszKeyName, const TCHAR *pszPath, BOOL blnVolatile)
|
|
{
|
|
if (!m_pCurrentKey)
|
|
{
|
|
SetErrorCommandNAOnRoot(_T("Creating new key "));
|
|
return FALSE;
|
|
}
|
|
|
|
CRegistryTree Tree(*this);
|
|
if (!Tree.ChangeCurrentKey(pszPath))
|
|
{
|
|
SetError(Tree.GetLastErrorDescription());
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL blnOpened;
|
|
HKEY hKey;
|
|
|
|
LONG nError = Tree.m_pCurrentKey->m_Key.CreateSubkey(KEY_READ,
|
|
pszKeyName,
|
|
hKey,
|
|
&blnOpened,
|
|
blnVolatile);
|
|
if (nError == ERROR_SUCCESS)
|
|
{
|
|
LONG nError = RegCloseKey(hKey);
|
|
ASSERT(nError == ERROR_SUCCESS);
|
|
}
|
|
|
|
if ((nError == ERROR_SUCCESS) && blnOpened)
|
|
{
|
|
SetError(_T("A key \"%s\" already exists."),pszKeyName);
|
|
return FALSE;
|
|
}
|
|
|
|
if (nError != ERROR_SUCCESS)
|
|
{
|
|
SetError(_T("Cannot create key : %s%s\nError %d (%s)\n"),
|
|
GetCurrentPath(),pszKeyName,nError,GetErrorDescription(nError));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CRegistryTree::DeleteSubkeys(const TCHAR *pszKeyPattern, const TCHAR *pszPath, BOOL blnRecursive)
|
|
{
|
|
CRegistryKey Key;
|
|
if (!GetKey(pszPath,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS|DELETE,Key))
|
|
return FALSE;
|
|
|
|
return DeleteSubkeys(Key, pszKeyPattern, blnRecursive);
|
|
}
|
|
|
|
BOOL CRegistryTree::DeleteSubkeys(CRegistryKey& rKey, const TCHAR *pszKeyPattern, BOOL blnRecursive)
|
|
{
|
|
LONG nError;
|
|
|
|
// enumerate subkeys
|
|
DWORD dwMaxSubkeyNameLength;
|
|
nError = rKey.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength);
|
|
if (nError != ERROR_SUCCESS)
|
|
{
|
|
SetError(_T("Cannot delete subkeys(s) of key %s.\nRequesting info about key failed.\nError %d (%s)\n"),
|
|
rKey.GetKeyName(),nError,GetErrorDescription(nError));
|
|
return FALSE;
|
|
}
|
|
|
|
TCHAR *pszSubkeyName = new TCHAR [dwMaxSubkeyNameLength];
|
|
rKey.InitSubkeyEnumeration(pszSubkeyName, dwMaxSubkeyNameLength);
|
|
BOOL blnKeyDeleted = FALSE;
|
|
while ((nError = rKey.GetNextSubkeyName()) == ERROR_SUCCESS)
|
|
{
|
|
if (PatternMatch(pszKeyPattern,pszSubkeyName))
|
|
{
|
|
if (blnRecursive)
|
|
{ // deltion is recursive, delete subkey subkeys
|
|
CRegistryKey Subkey;
|
|
// open subkey
|
|
nError = rKey.OpenSubkey(DELETE,pszSubkeyName,Subkey);
|
|
// delete subkey subkeys
|
|
if (DeleteSubkeys(Subkey, PATTERN_MATCH_ALL, TRUE))
|
|
{
|
|
AddErrorDescription(_T("Cannot delete subkey(s) of key %s. Subkey deletion failed.\n"),Subkey.GetKeyName());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
nError = rKey.DeleteSubkey(pszSubkeyName);
|
|
if (nError != ERROR_SUCCESS)
|
|
{
|
|
SetError(_T("Cannot delete the %s subkey of key %s.\nError %d (%s)\n"),
|
|
pszSubkeyName,rKey.GetKeyName(),nError,GetErrorDescription(nError));
|
|
|
|
return FALSE;
|
|
}
|
|
blnKeyDeleted = TRUE;
|
|
rKey.InitSubkeyEnumeration(pszSubkeyName, dwMaxSubkeyNameLength); // reset iteration
|
|
}
|
|
}
|
|
|
|
ASSERT(nError != ERROR_SUCCESS);
|
|
if (nError != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
SetError(_T("Cannot delete subkeys(s) of key %s.\nSubkey enumeration failed.\nError %d (%s)\n"),
|
|
rKey.GetKeyName(),nError,GetErrorDescription(nError));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!blnKeyDeleted)
|
|
SetError(_T("The key %s has no subkeys that match %s pattern.\n"),rKey.GetKeyName(),pszKeyPattern);
|
|
|
|
return blnKeyDeleted;
|
|
}
|
|
|
|
const TCHAR * CRegistryTree::GetErrorDescription(LONG nError)
|
|
{
|
|
switch(nError)
|
|
{
|
|
case ERROR_ACCESS_DENIED:
|
|
return _T("Access denied");
|
|
case ERROR_FILE_NOT_FOUND:
|
|
return _T("The system cannot find the key specified");
|
|
case ERROR_INTERNAL_ERROR:
|
|
return _T("Internal error");
|
|
case ERROR_OUTOFMEMORY:
|
|
return _T("Out of memory");
|
|
default:
|
|
return _T("Unknown error");
|
|
}
|
|
}
|
|
|
|
void CRegistryTree::SetError(LONG nError)
|
|
{
|
|
SetError(_T("Error %u (%s)"),nError,GetErrorDescription(nError));
|
|
}
|
|
|
|
void CRegistryTree::SetError(const TCHAR *pszFormat, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args,pszFormat);
|
|
if (_vsntprintf(m_ErrorMsg,ERROR_MSG_BUFFER_SIZE,pszFormat,args) < 0)
|
|
m_ErrorMsg[ERROR_MSG_BUFFER_SIZE] = 0;
|
|
va_end(args);
|
|
}
|
|
|
|
void CRegistryTree::SetInternalError()
|
|
{
|
|
SetError(_T("Internal Error"));
|
|
}
|
|
|
|
void CRegistryTree::AddErrorDescription(const TCHAR *pszFormat, ...)
|
|
{
|
|
size_t size = _tcslen(m_ErrorMsg);
|
|
if (size < ERROR_MSG_BUFFER_SIZE)
|
|
{
|
|
TCHAR *pszAdd = m_ErrorMsg+size;
|
|
va_list args;
|
|
va_start(args,pszFormat);
|
|
size = ERROR_MSG_BUFFER_SIZE-size;
|
|
if (_vsntprintf(pszAdd,size,pszFormat,args) < 0)
|
|
m_ErrorMsg[size] = 0;
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
void CRegistryTree::SetErrorCommandNAOnRoot(const TCHAR *pszCommand)
|
|
{
|
|
ASSERT(pszCommand);
|
|
if (pszCommand)
|
|
SetError(_T("%s") COMMAND_NA_ON_ROOT,pszCommand);
|
|
else
|
|
SetInternalError();
|
|
}
|
|
|
|
BOOL CRegistryTree::InternalChangeCurrentKey(const TCHAR *pszSubkeyName, REGSAM DesiredAccess)
|
|
{
|
|
size_t size = _tcslen(pszSubkeyName);
|
|
TCHAR *pszSubkeyNameBuffer = new TCHAR[size+3];
|
|
if (!pszSubkeyNameBuffer)
|
|
{
|
|
SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
|
|
GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY));
|
|
}
|
|
|
|
_tcscpy(pszSubkeyNameBuffer,pszSubkeyName);
|
|
if (size && (pszSubkeyName[0] == _T('\"')) && (pszSubkeyName[size-1] == _T('\"')))
|
|
{
|
|
pszSubkeyNameBuffer[size-1] = 0;
|
|
pszSubkeyName = pszSubkeyNameBuffer+1;
|
|
}
|
|
|
|
if (_tcscmp(pszSubkeyName,_T(".")) == 0)
|
|
{
|
|
delete pszSubkeyNameBuffer;
|
|
return TRUE;
|
|
}
|
|
|
|
if (_tcscmp(pszSubkeyName,_T("..")) == 0)
|
|
{
|
|
// Up level abstraction
|
|
if (m_pCurrentKey->m_Key.IsRoot())
|
|
{
|
|
// We are on root
|
|
ASSERT(m_pCurrentKey->m_pUp == NULL);
|
|
SetError(_T("Cannot open key. The root is not child.\n"));
|
|
delete pszSubkeyNameBuffer;
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT(m_pCurrentKey->m_pUp);
|
|
if (!m_pCurrentKey->m_pUp)
|
|
{
|
|
SetInternalError();
|
|
delete pszSubkeyNameBuffer;
|
|
return FALSE;
|
|
}
|
|
CNode *pNode = m_pCurrentKey;
|
|
m_pCurrentKey = m_pCurrentKey->m_pUp;
|
|
delete pNode;
|
|
delete pszSubkeyNameBuffer;
|
|
return TRUE;
|
|
}
|
|
|
|
CNode *pNewKey = new CNode;
|
|
if (!pNewKey)
|
|
{
|
|
SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
|
|
GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY));
|
|
delete pszSubkeyNameBuffer;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!InternalGetSubkey(pszSubkeyName,DesiredAccess,pNewKey->m_Key))
|
|
{
|
|
delete pNewKey;
|
|
delete pszSubkeyNameBuffer;
|
|
return FALSE;
|
|
}
|
|
pNewKey->m_pUp = m_pCurrentKey;
|
|
m_pCurrentKey = pNewKey;
|
|
|
|
delete pszSubkeyNameBuffer;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CRegistryTree::InternalGetSubkey(const TCHAR *pszSubkeyName, REGSAM DesiredAccess, CRegistryKey& rKey)
|
|
{
|
|
LONG nError;
|
|
HKEY hNewKey = NULL;
|
|
TCHAR *pszSubkeyNameCaseUpdated = NULL;
|
|
|
|
nError = m_pCurrentKey->m_Key.OpenSubkey(DesiredAccess,pszSubkeyName,hNewKey);
|
|
|
|
if (nError != ERROR_SUCCESS)
|
|
{
|
|
SetError(_T("Cannot open key : %s%s\nError %u (%s)\n"),
|
|
GetCurrentPath(),pszSubkeyName,nError,GetErrorDescription(nError));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// enum subkeys to find the subkey and get its name in stored case.
|
|
DWORD dwMaxSubkeyNameLength;
|
|
nError = m_pCurrentKey->m_Key.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength);
|
|
if (nError != ERROR_SUCCESS)
|
|
goto SkipCaseUpdate;
|
|
|
|
pszSubkeyNameCaseUpdated = new TCHAR [dwMaxSubkeyNameLength];
|
|
m_pCurrentKey->m_Key.InitSubkeyEnumeration(pszSubkeyNameCaseUpdated, dwMaxSubkeyNameLength);
|
|
while ((nError = m_pCurrentKey->m_Key.GetNextSubkeyName()) == ERROR_SUCCESS)
|
|
if (_tcsicmp(pszSubkeyNameCaseUpdated, pszSubkeyName) == 0)
|
|
break;
|
|
|
|
if (nError != ERROR_SUCCESS)
|
|
{
|
|
delete pszSubkeyNameCaseUpdated;
|
|
pszSubkeyNameCaseUpdated = NULL;
|
|
}
|
|
|
|
SkipCaseUpdate:
|
|
|
|
HRESULT hr;
|
|
ASSERT(hNewKey);
|
|
if (pszSubkeyNameCaseUpdated)
|
|
{
|
|
hr = rKey.Init(hNewKey,GetCurrentPath(),pszSubkeyNameCaseUpdated,DesiredAccess);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == (HRESULT)E_OUTOFMEMORY)
|
|
SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
|
|
GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY));
|
|
else
|
|
SetError(_T("Cannot open key : %s%s\nUnknown error\n"), GetCurrentPath(), pszSubkeyName);
|
|
|
|
goto Abort;
|
|
}
|
|
|
|
delete pszSubkeyNameCaseUpdated;
|
|
}
|
|
else
|
|
{
|
|
hr = rKey.Init(hNewKey,GetCurrentPath(),pszSubkeyName,DesiredAccess);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == (HRESULT)E_OUTOFMEMORY)
|
|
SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
|
|
GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY));
|
|
else
|
|
SetError(_T("Cannot open key : %s%s\nUnknown error \n"),
|
|
GetCurrentPath(),
|
|
pszSubkeyName);
|
|
|
|
goto Abort;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
Abort:
|
|
if (pszSubkeyNameCaseUpdated)
|
|
delete pszSubkeyNameCaseUpdated;
|
|
|
|
if (hNewKey)
|
|
{
|
|
LONG nError = RegCloseKey(hNewKey);
|
|
ASSERT(nError == ERROR_SUCCESS);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CRegistryTree::GetKey(const TCHAR *pszRelativePath, REGSAM DesiredAccess, CRegistryKey& rKey)
|
|
{
|
|
CRegistryTree Tree(*this);
|
|
|
|
if (!Tree.ChangeCurrentKey(pszRelativePath))
|
|
{
|
|
SetError(Tree.GetLastErrorDescription());
|
|
return FALSE;
|
|
}
|
|
|
|
if (Tree.m_pCurrentKey->m_Key.IsRoot())
|
|
{
|
|
HRESULT hr = rKey.InitRoot(m_pszMachineName);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == (HRESULT)E_OUTOFMEMORY)
|
|
SetError(_T("\nError %d (%s)\n"),
|
|
ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY));
|
|
else
|
|
SetInternalError();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// open key with desired access
|
|
|
|
// may be call to DuplicateHandle() is better.
|
|
// registry key handles returned by the RegConnectRegistry function cannot be used in a call to DuplicateHandle.
|
|
|
|
// Get short key name now...
|
|
const TCHAR *pszKeyName = Tree.m_pCurrentKey->m_Key.GetKeyName();
|
|
if (pszKeyName == NULL)
|
|
{
|
|
SetInternalError();
|
|
return FALSE;
|
|
}
|
|
|
|
size_t size = _tcslen(pszKeyName);
|
|
ASSERT(size);
|
|
if (!size)
|
|
{
|
|
SetInternalError();
|
|
return FALSE;
|
|
}
|
|
|
|
const TCHAR *pszShortKeyName_ = pszKeyName + size-1;
|
|
pszShortKeyName_--; // skip ending backslash
|
|
size = 0;
|
|
while (pszShortKeyName_ >= pszKeyName)
|
|
{
|
|
if (*pszShortKeyName_ == _T('\\'))
|
|
break;
|
|
pszShortKeyName_--;
|
|
size++;
|
|
}
|
|
|
|
if (!size || (*pszShortKeyName_ != _T('\\')))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetInternalError();
|
|
return FALSE;
|
|
}
|
|
|
|
TCHAR *pszShortKeyName = new TCHAR [size+1];
|
|
if (!pszShortKeyName)
|
|
{
|
|
SetError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(pszShortKeyName,pszShortKeyName_+1,size*sizeof(TCHAR));
|
|
pszShortKeyName[size] = 0;
|
|
|
|
// change to parent key
|
|
if (!Tree.InternalChangeCurrentKey(_T(".."),READ_CONTROL))
|
|
{
|
|
ASSERT(FALSE);
|
|
SetInternalError();
|
|
return FALSE;
|
|
}
|
|
|
|
// change back to target key
|
|
if (!Tree.InternalGetSubkey(pszShortKeyName,DesiredAccess,rKey))
|
|
{
|
|
SetError(Tree.GetLastErrorDescription());
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|