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