3 * regexpl - Console Registry Explorer
5 * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
23 // RegistryTree.cpp: implementation of the CRegistryTree class.
26 #include "RegistryTree.h"
28 #include "RegistryExplorer.h"
30 CRegistryTree::CRegistryTree()
32 m_pszMachineName
= NULL
;
33 VERIFY(SUCCEEDED(m_Root
.m_Key
.InitRoot()));
35 m_pCurrentKey
= &m_Root
;
36 ASSERT(m_pCurrentKey
->m_Key
.IsRoot());
37 m_ErrorMsg
[ERROR_MSG_BUFFER_SIZE
] = 0;
40 CRegistryTree::CRegistryTree(const CRegistryTree
& Tree
)
42 m_pszMachineName
= NULL
;
43 VERIFY(SUCCEEDED(m_Root
.m_Key
.InitRoot()));
45 m_pCurrentKey
= &m_Root
;
46 ASSERT(m_pCurrentKey
->m_Key
.IsRoot());
48 const TCHAR
*pszPath
= Tree
.GetCurrentPath();
49 if ((pszPath
[0] == _T('\\')) && (pszPath
[1] == _T('\\')))
50 { // path has machine name
52 while (*pszPath
&& (*pszPath
!= _T('\\')))
55 ASSERT(*pszPath
== _T('\\')); // if path begins with \\ it must be followed by machine name
58 if (Tree
.m_pszMachineName
)
59 SetMachineName(Tree
.m_pszMachineName
);
61 VERIFY(ChangeCurrentKey(pszPath
));
64 CRegistryTree::~CRegistryTree()
67 delete m_pszMachineName
;
70 while(m_pCurrentKey
->m_pUp
)
72 pNode
= m_pCurrentKey
;
73 m_pCurrentKey
= m_pCurrentKey
->m_pUp
;
78 ASSERT(m_pCurrentKey
->m_Key
.IsRoot());
79 ASSERT(m_pCurrentKey
== &m_Root
);
82 const TCHAR
* CRegistryTree::GetCurrentPath() const
84 return m_pCurrentKey
->m_Key
.GetKeyName();
87 BOOL
CRegistryTree::IsCurrentRoot()
89 return m_pCurrentKey
->m_Key
.IsRoot();
92 BOOL
CRegistryTree::ChangeCurrentKey(const TCHAR
*pszRelativePath
)
94 if (*pszRelativePath
== _T('\\'))
95 GotoRoot(); // This is full absolute path.
97 // split path to key names.
98 TCHAR
*pszSeps
= _T("\\");
100 // Make buffer and copy relative path into it.
101 TCHAR
*pszBuffer
= new TCHAR
[_tcslen(pszRelativePath
)+1];
104 SetError(ERROR_OUTOFMEMORY
);
108 _tcscpy(pszBuffer
,pszRelativePath
);
110 // We accept names in form "\"blablabla\\blab labla\"\\"
111 size_t size
= _tcslen(pszBuffer
);
114 if (pszBuffer
[size
-1] == _T('\\'))
115 pszBuffer
[--size
] = 0;
118 if (*pszBuffer
== _T('\"') && (psz
= _tcschr(pszBuffer
+1,_T('\"'))) && size_t(psz
-pszBuffer
) == size
-1)
127 TCHAR
*pszNewKey
= _tcstok(pszBuffer
,pszSeps
);
129 if ((!pszNewKey
)&&((*pszRelativePath
!= _T('\\'))||(*(pszRelativePath
+1) != 0)))
131 SetError(_T("Invalid key name"));
138 if (!InternalChangeCurrentKey(pszNewKey
,KEY_READ
))
139 goto Abort
; // InternalChangeCurrentKey sets last error description
142 pszNewKey
= _tcstok(NULL
,pszSeps
);
152 const TCHAR
* CRegistryTree::GetLastErrorDescription()
157 void CRegistryTree::GotoRoot()
159 // Delete current tree
161 while(m_pCurrentKey
->m_pUp
)
163 pNode
= m_pCurrentKey
;
164 m_pCurrentKey
= m_pCurrentKey
->m_pUp
;
169 ASSERT(m_pCurrentKey
->m_Key
.IsRoot());
170 ASSERT(m_pCurrentKey
== &m_Root
);
173 BOOL
CRegistryTree::SetMachineName(LPCTSTR pszMachineName
)
177 // If we are going to local machine...
178 if (pszMachineName
== NULL
)
180 // Delete previous machine name buffer if allocated.
181 if (m_pszMachineName
)
182 delete m_pszMachineName
;
184 m_pszMachineName
= NULL
;
185 m_Root
.m_Key
.InitRoot();
189 // Skip leading backslashes if any.
190 while ((*pszMachineName
)&&(*pszMachineName
== _T('\\')))
193 ASSERT(*pszMachineName
); // No machine name.
195 TCHAR
*pszNewMachineName
= new TCHAR
[_tcslen(pszMachineName
)+3]; // two leading backslashes + terminating null
199 SetError(ERROR_OUTOFMEMORY
);
203 // Delete previous machine name buffer if allocated.
204 if (m_pszMachineName
)
205 delete m_pszMachineName
;
207 m_pszMachineName
= pszNewMachineName
;
209 _tcscpy(m_pszMachineName
,_T("\\\\")); // leading backslashes
210 _tcscpy(m_pszMachineName
+2,pszMachineName
); // machine name itself
211 _tcsupr(m_pszMachineName
+2); // upercase it
213 VERIFY(SUCCEEDED(m_Root
.m_Key
.InitRoot(m_pszMachineName
)));
217 BOOL
CRegistryTree::NewKey(const TCHAR
*pszKeyName
, const TCHAR
*pszPath
, BOOL blnVolatile
)
221 SetErrorCommandNAOnRoot(_T("Creating new key "));
225 CRegistryTree
Tree(*this);
226 if (!Tree
.ChangeCurrentKey(pszPath
))
228 SetError(Tree
.GetLastErrorDescription());
235 LONG nError
= Tree
.m_pCurrentKey
->m_Key
.CreateSubkey(KEY_READ
,
240 if (nError
== ERROR_SUCCESS
)
242 LONG nError
= RegCloseKey(hKey
);
243 ASSERT(nError
== ERROR_SUCCESS
);
246 if ((nError
== ERROR_SUCCESS
) && blnOpened
)
248 SetError(_T("A key \"%s\" already exists."),pszKeyName
);
252 if (nError
!= ERROR_SUCCESS
)
254 SetError(_T("Cannot create key : %s%s\nError %d (%s)\n"),
255 GetCurrentPath(),pszKeyName
,nError
,GetErrorDescription(nError
));
263 BOOL
CRegistryTree::DeleteSubkeys(const TCHAR
*pszKeyPattern
, const TCHAR
*pszPath
, BOOL blnRecursive
)
266 if (!GetKey(pszPath
,KEY_QUERY_VALUE
|KEY_ENUMERATE_SUB_KEYS
|DELETE
,Key
))
269 return DeleteSubkeys(Key
, pszKeyPattern
, blnRecursive
);
272 BOOL
CRegistryTree::DeleteSubkeys(CRegistryKey
& rKey
, const TCHAR
*pszKeyPattern
, BOOL blnRecursive
)
277 DWORD dwMaxSubkeyNameLength
;
278 nError
= rKey
.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength
);
279 if (nError
!= ERROR_SUCCESS
)
281 SetError(_T("Cannot delete subkeys(s) of key %s.\nRequesting info about key failed.\nError %d (%s)\n"),
282 rKey
.GetKeyName(),nError
,GetErrorDescription(nError
));
286 TCHAR
*pszSubkeyName
= new TCHAR
[dwMaxSubkeyNameLength
];
287 rKey
.InitSubkeyEnumeration(pszSubkeyName
, dwMaxSubkeyNameLength
);
288 BOOL blnKeyDeleted
= FALSE
;
289 while ((nError
= rKey
.GetNextSubkeyName()) == ERROR_SUCCESS
)
291 if (PatternMatch(pszKeyPattern
,pszSubkeyName
))
294 { // deltion is recursive, delete subkey subkeys
297 nError
= rKey
.OpenSubkey(DELETE
,pszSubkeyName
,Subkey
);
298 // delete subkey subkeys
299 if (DeleteSubkeys(Subkey
, PATTERN_MATCH_ALL
, TRUE
))
301 AddErrorDescription(_T("Cannot delete subkey(s) of key %s. Subkey deletion failed.\n"),Subkey
.GetKeyName());
306 nError
= rKey
.DeleteSubkey(pszSubkeyName
);
307 if (nError
!= ERROR_SUCCESS
)
309 SetError(_T("Cannot delete the %s subkey of key %s.\nError %d (%s)\n"),
310 pszSubkeyName
,rKey
.GetKeyName(),nError
,GetErrorDescription(nError
));
314 blnKeyDeleted
= TRUE
;
315 rKey
.InitSubkeyEnumeration(pszSubkeyName
, dwMaxSubkeyNameLength
); // reset iteration
319 ASSERT(nError
!= ERROR_SUCCESS
);
320 if (nError
!= ERROR_NO_MORE_ITEMS
)
322 SetError(_T("Cannot delete subkeys(s) of key %s.\nSubkey enumeration failed.\nError %d (%s)\n"),
323 rKey
.GetKeyName(),nError
,GetErrorDescription(nError
));
328 SetError(_T("The key %s has no subkeys that match %s pattern.\n"),rKey
.GetKeyName(),pszKeyPattern
);
330 return blnKeyDeleted
;
333 const TCHAR
* CRegistryTree::GetErrorDescription(LONG nError
)
337 case ERROR_ACCESS_DENIED
:
338 return _T("Access denied");
339 case ERROR_FILE_NOT_FOUND
:
340 return _T("The system cannot find the key specified");
341 case ERROR_INTERNAL_ERROR
:
342 return _T("Internal error");
343 case ERROR_OUTOFMEMORY
:
344 return _T("Out of memory");
346 return _T("Unknown error");
350 void CRegistryTree::SetError(LONG nError
)
352 SetError(_T("Error %u (%s)"),nError
,GetErrorDescription(nError
));
355 void CRegistryTree::SetError(const TCHAR
*pszFormat
, ...)
358 va_start(args
,pszFormat
);
359 if (_vsntprintf(m_ErrorMsg
,ERROR_MSG_BUFFER_SIZE
,pszFormat
,args
) < 0)
360 m_ErrorMsg
[ERROR_MSG_BUFFER_SIZE
] = 0;
364 void CRegistryTree::SetInternalError()
366 SetError(_T("Internal Error"));
369 void CRegistryTree::AddErrorDescription(const TCHAR
*pszFormat
, ...)
371 size_t size
= _tcslen(m_ErrorMsg
);
372 if (size
< ERROR_MSG_BUFFER_SIZE
)
374 TCHAR
*pszAdd
= m_ErrorMsg
+size
;
376 va_start(args
,pszFormat
);
377 size
= ERROR_MSG_BUFFER_SIZE
-size
;
378 if (_vsntprintf(pszAdd
,size
,pszFormat
,args
) < 0)
379 m_ErrorMsg
[size
] = 0;
384 void CRegistryTree::SetErrorCommandNAOnRoot(const TCHAR
*pszCommand
)
388 SetError(_T("%s") COMMAND_NA_ON_ROOT
,pszCommand
);
393 BOOL
CRegistryTree::InternalChangeCurrentKey(const TCHAR
*pszSubkeyName
, REGSAM DesiredAccess
)
395 size_t size
= _tcslen(pszSubkeyName
);
396 TCHAR
*pszSubkeyNameBuffer
= new TCHAR
[size
+3];
397 if (!pszSubkeyNameBuffer
)
399 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
400 GetCurrentPath(),pszSubkeyName
,ERROR_OUTOFMEMORY
,GetErrorDescription(ERROR_OUTOFMEMORY
));
403 _tcscpy(pszSubkeyNameBuffer
,pszSubkeyName
);
404 if (size
&& (pszSubkeyName
[0] == _T('\"')) && (pszSubkeyName
[size
-1] == _T('\"')))
406 pszSubkeyNameBuffer
[size
-1] = 0;
407 pszSubkeyName
= pszSubkeyNameBuffer
+1;
410 if (_tcscmp(pszSubkeyName
,_T(".")) == 0)
412 delete pszSubkeyNameBuffer
;
416 if (_tcscmp(pszSubkeyName
,_T("..")) == 0)
418 // Up level abstraction
419 if (m_pCurrentKey
->m_Key
.IsRoot())
422 ASSERT(m_pCurrentKey
->m_pUp
== NULL
);
423 SetError(_T("Cannot open key. The root is not child.\n"));
424 delete pszSubkeyNameBuffer
;
428 ASSERT(m_pCurrentKey
->m_pUp
);
429 if (!m_pCurrentKey
->m_pUp
)
432 delete pszSubkeyNameBuffer
;
435 CNode
*pNode
= m_pCurrentKey
;
436 m_pCurrentKey
= m_pCurrentKey
->m_pUp
;
438 delete pszSubkeyNameBuffer
;
442 CNode
*pNewKey
= new CNode
;
445 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
446 GetCurrentPath(),pszSubkeyName
,ERROR_OUTOFMEMORY
,GetErrorDescription(ERROR_OUTOFMEMORY
));
447 delete pszSubkeyNameBuffer
;
451 if (!InternalGetSubkey(pszSubkeyName
,DesiredAccess
,pNewKey
->m_Key
))
454 delete pszSubkeyNameBuffer
;
457 pNewKey
->m_pUp
= m_pCurrentKey
;
458 m_pCurrentKey
= pNewKey
;
460 delete pszSubkeyNameBuffer
;
464 BOOL
CRegistryTree::InternalGetSubkey(const TCHAR
*pszSubkeyName
, REGSAM DesiredAccess
, CRegistryKey
& rKey
)
468 TCHAR
*pszSubkeyNameCaseUpdated
= NULL
;
470 nError
= m_pCurrentKey
->m_Key
.OpenSubkey(DesiredAccess
,pszSubkeyName
,hNewKey
);
472 if (nError
!= ERROR_SUCCESS
)
474 SetError(_T("Cannot open key : %s%s\nError %u (%s)\n"),
475 GetCurrentPath(),pszSubkeyName
,nError
,GetErrorDescription(nError
));
480 // enum subkeys to find the subkey and get its name in stored case.
481 DWORD dwMaxSubkeyNameLength
;
482 nError
= m_pCurrentKey
->m_Key
.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength
);
483 if (nError
!= ERROR_SUCCESS
)
486 pszSubkeyNameCaseUpdated
= new TCHAR
[dwMaxSubkeyNameLength
];
487 m_pCurrentKey
->m_Key
.InitSubkeyEnumeration(pszSubkeyNameCaseUpdated
, dwMaxSubkeyNameLength
);
488 while ((nError
= m_pCurrentKey
->m_Key
.GetNextSubkeyName()) == ERROR_SUCCESS
)
489 if (_tcsicmp(pszSubkeyNameCaseUpdated
, pszSubkeyName
) == 0)
492 if (nError
!= ERROR_SUCCESS
)
494 delete pszSubkeyNameCaseUpdated
;
495 pszSubkeyNameCaseUpdated
= NULL
;
502 if (pszSubkeyNameCaseUpdated
)
504 hr
= rKey
.Init(hNewKey
,GetCurrentPath(),pszSubkeyNameCaseUpdated
,DesiredAccess
);
507 if (hr
== (HRESULT
)E_OUTOFMEMORY
)
508 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
509 GetCurrentPath(),pszSubkeyName
,ERROR_OUTOFMEMORY
,GetErrorDescription(ERROR_OUTOFMEMORY
));
511 SetError(_T("Cannot open key : %s%s\nUnknown error\n"), GetCurrentPath(), pszSubkeyName
);
516 delete pszSubkeyNameCaseUpdated
;
520 hr
= rKey
.Init(hNewKey
,GetCurrentPath(),pszSubkeyName
,DesiredAccess
);
523 if (hr
== (HRESULT
)E_OUTOFMEMORY
)
524 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"),
525 GetCurrentPath(),pszSubkeyName
,ERROR_OUTOFMEMORY
,GetErrorDescription(ERROR_OUTOFMEMORY
));
527 SetError(_T("Cannot open key : %s%s\nUnknown error \n"),
537 if (pszSubkeyNameCaseUpdated
)
538 delete pszSubkeyNameCaseUpdated
;
542 LONG nError
= RegCloseKey(hNewKey
);
543 ASSERT(nError
== ERROR_SUCCESS
);
549 BOOL
CRegistryTree::GetKey(const TCHAR
*pszRelativePath
, REGSAM DesiredAccess
, CRegistryKey
& rKey
)
551 CRegistryTree
Tree(*this);
553 if (!Tree
.ChangeCurrentKey(pszRelativePath
))
555 SetError(Tree
.GetLastErrorDescription());
559 if (Tree
.m_pCurrentKey
->m_Key
.IsRoot())
561 HRESULT hr
= rKey
.InitRoot(m_pszMachineName
);
564 if (hr
== (HRESULT
)E_OUTOFMEMORY
)
565 SetError(_T("\nError %d (%s)\n"),
566 ERROR_OUTOFMEMORY
,GetErrorDescription(ERROR_OUTOFMEMORY
));
575 // open key with desired access
577 // may be call to DuplicateHandle() is better.
578 // registry key handles returned by the RegConnectRegistry function cannot be used in a call to DuplicateHandle.
580 // Get short key name now...
581 const TCHAR
*pszKeyName
= Tree
.m_pCurrentKey
->m_Key
.GetKeyName();
582 if (pszKeyName
== NULL
)
588 size_t size
= _tcslen(pszKeyName
);
596 const TCHAR
*pszShortKeyName_
= pszKeyName
+ size
-1;
597 pszShortKeyName_
--; // skip ending backslash
599 while (pszShortKeyName_
>= pszKeyName
)
601 if (*pszShortKeyName_
== _T('\\'))
607 if (!size
|| (*pszShortKeyName_
!= _T('\\')))
614 TCHAR
*pszShortKeyName
= new TCHAR
[size
+1];
615 if (!pszShortKeyName
)
617 SetError(ERROR_OUTOFMEMORY
);
621 memcpy(pszShortKeyName
,pszShortKeyName_
+1,size
*sizeof(TCHAR
));
622 pszShortKeyName
[size
] = 0;
624 // change to parent key
625 if (!Tree
.InternalChangeCurrentKey(_T(".."),READ_CONTROL
))
632 // change back to target key
633 if (!Tree
.InternalGetSubkey(pszShortKeyName
,DesiredAccess
,rKey
))
635 SetError(Tree
.GetLastErrorDescription());