1 /* 2 * regexpl - Console Registry Explorer 3 * 4 * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 * Boston, MA 02111-1307, USA. 20 */ 21 22 // RegistryTree.cpp: implementation of the CRegistryTree class. 23 24 #include "ph.h" 25 #include "RegistryTree.h" 26 #include "Pattern.h" 27 #include "RegistryExplorer.h" 28 29 CRegistryTree::CRegistryTree() 30 { 31 m_pszMachineName = NULL; 32 VERIFY(SUCCEEDED(m_Root.m_Key.InitRoot())); 33 m_Root.m_pUp = NULL; 34 m_pCurrentKey = &m_Root; 35 ASSERT(m_pCurrentKey->m_Key.IsRoot()); 36 m_ErrorMsg[ERROR_MSG_BUFFER_SIZE] = 0; 37 } 38 39 CRegistryTree::CRegistryTree(const CRegistryTree& Tree) 40 { 41 m_pszMachineName = NULL; 42 VERIFY(SUCCEEDED(m_Root.m_Key.InitRoot())); 43 m_Root.m_pUp = NULL; 44 m_pCurrentKey = &m_Root; 45 ASSERT(m_pCurrentKey->m_Key.IsRoot()); 46 47 const TCHAR *pszPath = Tree.GetCurrentPath(); 48 if ((pszPath[0] == _T('\\')) && (pszPath[1] == _T('\\'))) 49 { // path has machine name 50 pszPath += 2; 51 while (*pszPath && (*pszPath != _T('\\'))) 52 pszPath++; 53 54 ASSERT(*pszPath == _T('\\')); // if path begins with \\ it must be followed by machine name 55 } 56 57 if (Tree.m_pszMachineName) 58 SetMachineName(Tree.m_pszMachineName); 59 60 VERIFY(ChangeCurrentKey(pszPath)); 61 } 62 63 CRegistryTree::~CRegistryTree() 64 { 65 if (m_pszMachineName) 66 delete[] m_pszMachineName; 67 68 CNode *pNode; 69 while(m_pCurrentKey->m_pUp) 70 { 71 pNode = m_pCurrentKey; 72 m_pCurrentKey = m_pCurrentKey->m_pUp; 73 delete pNode; 74 } 75 76 // We are on root 77 ASSERT(m_pCurrentKey->m_Key.IsRoot()); 78 ASSERT(m_pCurrentKey == &m_Root); 79 } 80 81 const TCHAR * CRegistryTree::GetCurrentPath() const 82 { 83 return m_pCurrentKey->m_Key.GetKeyName(); 84 } 85 86 BOOL CRegistryTree::IsCurrentRoot() 87 { 88 return m_pCurrentKey->m_Key.IsRoot(); 89 } 90 91 BOOL CRegistryTree::ChangeCurrentKey(const TCHAR *pszRelativePath) 92 { 93 if (*pszRelativePath == _T('\\')) 94 GotoRoot(); // This is full absolute path. 95 96 // split path to key names. 97 const TCHAR *pszSeps = _T("\\"); 98 99 // Make buffer and copy relative path into it. 100 TCHAR *pszBuffer = new (std::nothrow) TCHAR[_tcslen(pszRelativePath)+1]; 101 if (!pszBuffer) 102 { 103 SetError(ERROR_OUTOFMEMORY); 104 return FALSE; 105 } 106 107 _tcscpy(pszBuffer,pszRelativePath); 108 109 // We accept names in form "\"blablabla\\blab labla\"\\" 110 size_t size = _tcslen(pszBuffer); 111 if (size) 112 { 113 if (pszBuffer[size-1] == _T('\\')) 114 pszBuffer[--size] = 0; 115 116 TCHAR *psz; 117 if (*pszBuffer == _T('\"') && (psz = _tcschr(pszBuffer+1,_T('\"'))) && size_t(psz-pszBuffer) == size-1) 118 { 119 size--; 120 pszBuffer[size] = 0; 121 122 pszBuffer++; 123 } 124 } 125 126 const TCHAR *pszNewKey = _tcstok(pszBuffer,pszSeps); 127 128 if ((!pszNewKey)&&((*pszRelativePath != _T('\\'))||(*(pszRelativePath+1) != 0))) 129 { 130 SetError(_T("Invalid key name")); 131 goto Abort; 132 }; 133 134 // change keys 135 while (pszNewKey) 136 { 137 if (!InternalChangeCurrentKey(pszNewKey,KEY_READ)) 138 goto Abort; // InternalChangeCurrentKey sets last error description 139 140 // Get next key name 141 pszNewKey = _tcstok(NULL,pszSeps); 142 } 143 144 return TRUE; 145 146 Abort: 147 delete[] pszBuffer; 148 return FALSE; 149 } 150 151 const TCHAR * CRegistryTree::GetLastErrorDescription() 152 { 153 return m_ErrorMsg; 154 } 155 156 void CRegistryTree::GotoRoot() 157 { 158 // Delete current tree 159 CNode *pNode; 160 while(m_pCurrentKey->m_pUp) 161 { 162 pNode = m_pCurrentKey; 163 m_pCurrentKey = m_pCurrentKey->m_pUp; 164 delete pNode; 165 } 166 167 // We are on root 168 ASSERT(m_pCurrentKey->m_Key.IsRoot()); 169 ASSERT(m_pCurrentKey == &m_Root); 170 } 171 172 BOOL CRegistryTree::SetMachineName(LPCTSTR pszMachineName) 173 { 174 GotoRoot(); 175 176 // If we are going to local machine... 177 if (pszMachineName == NULL) 178 { 179 // Delete previous machine name buffer if allocated. 180 if (m_pszMachineName) 181 delete[] m_pszMachineName; 182 183 m_pszMachineName = NULL; 184 m_Root.m_Key.InitRoot(); 185 return TRUE; 186 } 187 188 // Skip leading backslashes if any. 189 while ((*pszMachineName)&&(*pszMachineName == _T('\\'))) 190 pszMachineName++; 191 192 ASSERT(*pszMachineName); // No machine name. 193 194 TCHAR *pszNewMachineName = new (std::nothrow) TCHAR[_tcslen(pszMachineName)+3]; // two leading backslashes + terminating null 195 196 if (!pszNewMachineName) 197 { 198 SetError(ERROR_OUTOFMEMORY); 199 return FALSE; 200 } 201 202 // Delete previous machine name buffer if allocated. 203 if (m_pszMachineName) 204 delete[] m_pszMachineName; 205 206 m_pszMachineName = pszNewMachineName; 207 208 _tcscpy(m_pszMachineName,_T("\\\\")); // leading backslashes 209 _tcscpy(m_pszMachineName+2,pszMachineName); // machine name itself 210 _tcsupr(m_pszMachineName+2); // upercase it 211 212 VERIFY(SUCCEEDED(m_Root.m_Key.InitRoot(m_pszMachineName))); 213 return TRUE; 214 } 215 216 BOOL CRegistryTree::NewKey(const TCHAR *pszKeyName, const TCHAR *pszPath, BOOL blnVolatile) 217 { 218 if (!m_pCurrentKey) 219 { 220 SetErrorCommandNAOnRoot(_T("Creating new key ")); 221 return FALSE; 222 } 223 224 CRegistryTree Tree(*this); 225 if (!Tree.ChangeCurrentKey(pszPath)) 226 { 227 SetError(Tree.GetLastErrorDescription()); 228 return FALSE; 229 } 230 231 BOOL blnOpened; 232 HKEY hKey; 233 234 LONG nError = Tree.m_pCurrentKey->m_Key.CreateSubkey(KEY_READ, 235 pszKeyName, 236 hKey, 237 &blnOpened, 238 blnVolatile); 239 if (nError == ERROR_SUCCESS) 240 { 241 LONG nError = RegCloseKey(hKey); 242 ASSERT(nError == ERROR_SUCCESS); 243 } 244 245 if ((nError == ERROR_SUCCESS) && blnOpened) 246 { 247 SetError(_T("A key \"%s\" already exists."),pszKeyName); 248 return FALSE; 249 } 250 251 if (nError != ERROR_SUCCESS) 252 { 253 SetError(_T("Cannot create key : %s%s\nError %d (%s)\n"), 254 GetCurrentPath(),pszKeyName,nError,GetErrorDescription(nError)); 255 256 return FALSE; 257 } 258 259 return TRUE; 260 } 261 262 BOOL CRegistryTree::DeleteSubkeys(const TCHAR *pszKeyPattern, const TCHAR *pszPath, BOOL blnRecursive) 263 { 264 CRegistryKey Key; 265 if (!GetKey(pszPath,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS|DELETE,Key)) 266 return FALSE; 267 268 return DeleteSubkeys(Key, pszKeyPattern, blnRecursive); 269 } 270 271 BOOL CRegistryTree::DeleteSubkeys(CRegistryKey& rKey, const TCHAR *pszKeyPattern, BOOL blnRecursive) 272 { 273 LONG nError; 274 275 // enumerate subkeys 276 DWORD dwMaxSubkeyNameLength; 277 nError = rKey.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength); 278 if (nError != ERROR_SUCCESS) 279 { 280 SetError(_T("Cannot delete subkeys(s) of key %s.\nRequesting info about key failed.\nError %d (%s)\n"), 281 rKey.GetKeyName(),nError,GetErrorDescription(nError)); 282 return FALSE; 283 } 284 285 TCHAR *pszSubkeyName = new (std::nothrow) TCHAR [dwMaxSubkeyNameLength]; 286 rKey.InitSubkeyEnumeration(pszSubkeyName, dwMaxSubkeyNameLength); 287 BOOL blnKeyDeleted = FALSE; 288 while ((nError = rKey.GetNextSubkeyName()) == ERROR_SUCCESS) 289 { 290 if (PatternMatch(pszKeyPattern,pszSubkeyName)) 291 { 292 if (blnRecursive) 293 { // deltion is recursive, delete subkey subkeys 294 CRegistryKey Subkey; 295 // open subkey 296 nError = rKey.OpenSubkey(DELETE,pszSubkeyName,Subkey); 297 // delete subkey subkeys 298 if (DeleteSubkeys(Subkey, PATTERN_MATCH_ALL, TRUE)) 299 { 300 AddErrorDescription(_T("Cannot delete subkey(s) of key %s. Subkey deletion failed.\n"),Subkey.GetKeyName()); 301 return FALSE; 302 } 303 } 304 305 nError = rKey.DeleteSubkey(pszSubkeyName); 306 if (nError != ERROR_SUCCESS) 307 { 308 SetError(_T("Cannot delete the %s subkey of key %s.\nError %d (%s)\n"), 309 pszSubkeyName,rKey.GetKeyName(),nError,GetErrorDescription(nError)); 310 311 return FALSE; 312 } 313 blnKeyDeleted = TRUE; 314 rKey.InitSubkeyEnumeration(pszSubkeyName, dwMaxSubkeyNameLength); // reset iteration 315 } 316 } 317 318 ASSERT(nError != ERROR_SUCCESS); 319 if (nError != ERROR_NO_MORE_ITEMS) 320 { 321 SetError(_T("Cannot delete subkeys(s) of key %s.\nSubkey enumeration failed.\nError %d (%s)\n"), 322 rKey.GetKeyName(),nError,GetErrorDescription(nError)); 323 return FALSE; 324 } 325 326 if (!blnKeyDeleted) 327 SetError(_T("The key %s has no subkeys that match %s pattern.\n"),rKey.GetKeyName(),pszKeyPattern); 328 329 return blnKeyDeleted; 330 } 331 332 const TCHAR * CRegistryTree::GetErrorDescription(LONG nError) 333 { 334 switch(nError) 335 { 336 case ERROR_ACCESS_DENIED: 337 return _T("Access denied"); 338 case ERROR_FILE_NOT_FOUND: 339 return _T("The system cannot find the key specified"); 340 case ERROR_INTERNAL_ERROR: 341 return _T("Internal error"); 342 case ERROR_OUTOFMEMORY: 343 return _T("Out of memory"); 344 default: 345 return _T("Unknown error"); 346 } 347 } 348 349 void CRegistryTree::SetError(LONG nError) 350 { 351 SetError(_T("Error %u (%s)"),nError,GetErrorDescription(nError)); 352 } 353 354 void CRegistryTree::SetError(const TCHAR *pszFormat, ...) 355 { 356 va_list args; 357 va_start(args,pszFormat); 358 if (_vsntprintf(m_ErrorMsg,ERROR_MSG_BUFFER_SIZE,pszFormat,args) < 0) 359 m_ErrorMsg[ERROR_MSG_BUFFER_SIZE] = 0; 360 va_end(args); 361 } 362 363 void CRegistryTree::SetInternalError() 364 { 365 SetError(_T("Internal Error")); 366 } 367 368 void CRegistryTree::AddErrorDescription(const TCHAR *pszFormat, ...) 369 { 370 size_t size = _tcslen(m_ErrorMsg); 371 if (size < ERROR_MSG_BUFFER_SIZE) 372 { 373 TCHAR *pszAdd = m_ErrorMsg+size; 374 va_list args; 375 va_start(args,pszFormat); 376 size = ERROR_MSG_BUFFER_SIZE-size; 377 if (_vsntprintf(pszAdd,size,pszFormat,args) < 0) 378 m_ErrorMsg[size] = 0; 379 va_end(args); 380 } 381 } 382 383 void CRegistryTree::SetErrorCommandNAOnRoot(const TCHAR *pszCommand) 384 { 385 ASSERT(pszCommand); 386 if (pszCommand) 387 SetError(_T("%s") COMMAND_NA_ON_ROOT,pszCommand); 388 else 389 SetInternalError(); 390 } 391 392 BOOL CRegistryTree::InternalChangeCurrentKey(const TCHAR *pszSubkeyName, REGSAM DesiredAccess) 393 { 394 size_t size = _tcslen(pszSubkeyName); 395 TCHAR *pszSubkeyNameBuffer = new (std::nothrow) TCHAR[size+3]; 396 if (!pszSubkeyNameBuffer) 397 { 398 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"), 399 GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY)); 400 } 401 402 _tcscpy(pszSubkeyNameBuffer,pszSubkeyName); 403 if (size && (pszSubkeyName[0] == _T('\"')) && (pszSubkeyName[size-1] == _T('\"'))) 404 { 405 pszSubkeyNameBuffer[size-1] = 0; 406 pszSubkeyName = pszSubkeyNameBuffer+1; 407 } 408 409 if (_tcscmp(pszSubkeyName,_T(".")) == 0) 410 { 411 delete[] pszSubkeyNameBuffer; 412 return TRUE; 413 } 414 415 if (_tcscmp(pszSubkeyName,_T("..")) == 0) 416 { 417 // Up level abstraction 418 if (m_pCurrentKey->m_Key.IsRoot()) 419 { 420 // We are on root 421 ASSERT(m_pCurrentKey->m_pUp == NULL); 422 SetError(_T("Cannot open key. The root is not child.\n")); 423 delete[] pszSubkeyNameBuffer; 424 return FALSE; 425 } 426 427 ASSERT(m_pCurrentKey->m_pUp); 428 if (!m_pCurrentKey->m_pUp) 429 { 430 SetInternalError(); 431 delete[] pszSubkeyNameBuffer; 432 return FALSE; 433 } 434 CNode *pNode = m_pCurrentKey; 435 m_pCurrentKey = m_pCurrentKey->m_pUp; 436 delete pNode; 437 delete[] pszSubkeyNameBuffer; 438 return TRUE; 439 } 440 441 CNode *pNewKey = new CNode; 442 if (!pNewKey) 443 { 444 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"), 445 GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY)); 446 delete[] pszSubkeyNameBuffer; 447 return FALSE; 448 } 449 450 if (!InternalGetSubkey(pszSubkeyName,DesiredAccess,pNewKey->m_Key)) 451 { 452 delete pNewKey; 453 delete[] pszSubkeyNameBuffer; 454 return FALSE; 455 } 456 pNewKey->m_pUp = m_pCurrentKey; 457 m_pCurrentKey = pNewKey; 458 459 delete[] pszSubkeyNameBuffer; 460 return TRUE; 461 } 462 463 BOOL CRegistryTree::InternalGetSubkey(const TCHAR *pszSubkeyName, REGSAM DesiredAccess, CRegistryKey& rKey) 464 { 465 LONG nError; 466 HKEY hNewKey = NULL; 467 TCHAR *pszSubkeyNameCaseUpdated = NULL; 468 469 nError = m_pCurrentKey->m_Key.OpenSubkey(DesiredAccess,pszSubkeyName,hNewKey); 470 471 if (nError != ERROR_SUCCESS) 472 { 473 SetError(_T("Cannot open key : %s%s\nError %u (%s)\n"), 474 GetCurrentPath(),pszSubkeyName,nError,GetErrorDescription(nError)); 475 476 return FALSE; 477 } 478 479 // enum subkeys to find the subkey and get its name in stored case. 480 DWORD dwMaxSubkeyNameLength; 481 nError = m_pCurrentKey->m_Key.GetSubkeyNameMaxLength(dwMaxSubkeyNameLength); 482 if (nError != ERROR_SUCCESS) 483 goto SkipCaseUpdate; 484 485 pszSubkeyNameCaseUpdated = new (std::nothrow) TCHAR [dwMaxSubkeyNameLength]; 486 m_pCurrentKey->m_Key.InitSubkeyEnumeration(pszSubkeyNameCaseUpdated, dwMaxSubkeyNameLength); 487 while ((nError = m_pCurrentKey->m_Key.GetNextSubkeyName()) == ERROR_SUCCESS) 488 if (_tcsicmp(pszSubkeyNameCaseUpdated, pszSubkeyName) == 0) 489 break; 490 491 if (nError != ERROR_SUCCESS) 492 { 493 delete[] pszSubkeyNameCaseUpdated; 494 pszSubkeyNameCaseUpdated = NULL; 495 } 496 497 SkipCaseUpdate: 498 499 HRESULT hr; 500 ASSERT(hNewKey); 501 if (pszSubkeyNameCaseUpdated) 502 { 503 hr = rKey.Init(hNewKey,GetCurrentPath(),pszSubkeyNameCaseUpdated,DesiredAccess); 504 if (FAILED(hr)) 505 { 506 if (hr == (HRESULT)E_OUTOFMEMORY) 507 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"), 508 GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY)); 509 else 510 SetError(_T("Cannot open key : %s%s\nUnknown error\n"), GetCurrentPath(), pszSubkeyName); 511 512 goto Abort; 513 } 514 515 delete[] pszSubkeyNameCaseUpdated; 516 } 517 else 518 { 519 hr = rKey.Init(hNewKey,GetCurrentPath(),pszSubkeyName,DesiredAccess); 520 if (FAILED(hr)) 521 { 522 if (hr == (HRESULT)E_OUTOFMEMORY) 523 SetError(_T("Cannot open key : %s%s\nError %d (%s)\n"), 524 GetCurrentPath(),pszSubkeyName,ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY)); 525 else 526 SetError(_T("Cannot open key : %s%s\nUnknown error \n"), 527 GetCurrentPath(), 528 pszSubkeyName); 529 530 goto Abort; 531 } 532 } 533 534 return TRUE; 535 Abort: 536 if (pszSubkeyNameCaseUpdated) 537 delete[] pszSubkeyNameCaseUpdated; 538 539 if (hNewKey) 540 { 541 LONG nError = RegCloseKey(hNewKey); 542 ASSERT(nError == ERROR_SUCCESS); 543 } 544 545 return FALSE; 546 } 547 548 BOOL CRegistryTree::GetKey(const TCHAR *pszRelativePath, REGSAM DesiredAccess, CRegistryKey& rKey) 549 { 550 CRegistryTree Tree(*this); 551 552 if (!Tree.ChangeCurrentKey(pszRelativePath)) 553 { 554 SetError(Tree.GetLastErrorDescription()); 555 return FALSE; 556 } 557 558 if (Tree.m_pCurrentKey->m_Key.IsRoot()) 559 { 560 HRESULT hr = rKey.InitRoot(m_pszMachineName); 561 if (FAILED(hr)) 562 { 563 if (hr == (HRESULT)E_OUTOFMEMORY) 564 SetError(_T("\nError %d (%s)\n"), 565 ERROR_OUTOFMEMORY,GetErrorDescription(ERROR_OUTOFMEMORY)); 566 else 567 SetInternalError(); 568 return FALSE; 569 } 570 571 return TRUE; 572 } 573 574 // open key with desired access 575 576 // may be call to DuplicateHandle() is better. 577 // registry key handles returned by the RegConnectRegistry function cannot be used in a call to DuplicateHandle. 578 579 // Get short key name now... 580 const TCHAR *pszKeyName = Tree.m_pCurrentKey->m_Key.GetKeyName(); 581 if (pszKeyName == NULL) 582 { 583 SetInternalError(); 584 return FALSE; 585 } 586 587 size_t size = _tcslen(pszKeyName); 588 ASSERT(size); 589 if (!size) 590 { 591 SetInternalError(); 592 return FALSE; 593 } 594 595 const TCHAR *pszShortKeyName_ = pszKeyName + size-1; 596 pszShortKeyName_--; // skip ending backslash 597 size = 0; 598 while (pszShortKeyName_ >= pszKeyName) 599 { 600 if (*pszShortKeyName_ == _T('\\')) 601 break; 602 pszShortKeyName_--; 603 size++; 604 } 605 606 if (!size || (*pszShortKeyName_ != _T('\\'))) 607 { 608 ASSERT(FALSE); 609 SetInternalError(); 610 return FALSE; 611 } 612 613 TCHAR *pszShortKeyName = new (std::nothrow) TCHAR [size+1]; 614 if (!pszShortKeyName) 615 { 616 SetError(ERROR_OUTOFMEMORY); 617 return FALSE; 618 } 619 620 memcpy(pszShortKeyName,pszShortKeyName_+1,size*sizeof(TCHAR)); 621 pszShortKeyName[size] = 0; 622 623 // change to parent key 624 if (!Tree.InternalChangeCurrentKey(_T(".."),READ_CONTROL)) 625 { 626 delete[] pszShortKeyName; 627 ASSERT(FALSE); 628 SetInternalError(); 629 return FALSE; 630 } 631 632 // change back to target key 633 if (!Tree.InternalGetSubkey(pszShortKeyName,DesiredAccess,rKey)) 634 { 635 delete[] pszShortKeyName; 636 SetError(Tree.GetLastErrorDescription()); 637 return FALSE; 638 } 639 640 delete[] pszShortKeyName; 641 return TRUE; 642 } 643 644