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 // RegistryKey.cpp: implementation of the CRegistryKey class.
23 //
24 //////////////////////////////////////////////////////////////////////
25
26 #include "ph.h"
27 #include "RegistryKey.h"
28
29 static const TCHAR *g_ppszHiveNames[] =
30 {
31 _T("HKEY_CLASSES_ROOT"),
32 _T("HKEY_CURRENT_USER"),
33 _T("HKEY_LOCAL_MACHINE"),
34 _T("HKEY_USERS"),
35 _T("HKEY_PERFORMANCE_DATA"),
36 _T("HKEY_CURRENT_CONFIG"),
37 _T("HKEY_DYN_DATA")
38 };
39
40 //////////////////////////////////////////////////////////////////////
41 // Construction/Destruction
42 //////////////////////////////////////////////////////////////////////
43
CRegistryKey()44 CRegistryKey::CRegistryKey()
45 {
46 m_pszKeyName = NULL;
47 m_pszMachineName = NULL;
48 m_CurrentAccess = 0;
49 m_hKey = NULL;
50 }
51
InitRoot(const TCHAR * pszMachineName)52 HRESULT CRegistryKey::InitRoot(const TCHAR *pszMachineName)
53 {
54 if ((pszMachineName)&&
55 ((_tcslen(pszMachineName) < 3)||
56 (pszMachineName[0] != _T('\\'))||
57 (pszMachineName[1] != _T('\\'))
58 )
59 )
60 {
61 return E_INVALIDARG;
62 }
63
64 HRESULT hr = Uninit();
65 if (FAILED(hr))
66 return hr;
67
68 if (pszMachineName)
69 { // copy machine name
70 size_t size = _tcslen(pszMachineName);
71
72 m_pszMachineName = new (std::nothrow) TCHAR [size+2];
73 if (!m_pszMachineName)
74 return E_OUTOFMEMORY;
75 _tcscpy(m_pszMachineName,pszMachineName);
76 m_pszMachineName[size] = _T('\\');
77 m_pszMachineName[size+1] = 0;
78 }
79 else
80 {
81 m_pszMachineName = NULL; // local registry
82 }
83
84 ASSERT(m_pszKeyName == NULL);
85 m_CurrentAccess = 0;
86 ASSERT(m_hKey == NULL);
87
88 return S_OK;
89 }
90
Init(HKEY hKey,const TCHAR * pszPath,const TCHAR * pszKeyName,REGSAM CurrentAccess)91 HRESULT CRegistryKey::Init(HKEY hKey, const TCHAR *pszPath, const TCHAR *pszKeyName, REGSAM CurrentAccess)
92 {
93 HRESULT hr = Uninit();
94 if (FAILED(hr))
95 return hr;
96
97 if (!pszKeyName || !hKey)
98 return E_INVALIDARG;
99
100 // copy key name name
101 size_t size = _tcslen(pszKeyName);
102 if (pszPath)
103 size += _tcslen(pszPath);
104
105 m_pszKeyName = new (std::nothrow) TCHAR [size+2];
106 if (!m_pszKeyName)
107 return E_OUTOFMEMORY;
108 _stprintf(m_pszKeyName,_T("%s%s\\"),pszPath?pszPath:_T(""),pszKeyName);
109
110 m_CurrentAccess = CurrentAccess;
111 m_hKey = hKey;
112 ASSERT(m_hKey);
113
114 return S_OK;
115 }
116
Uninit()117 HRESULT CRegistryKey::Uninit()
118 {
119 if (m_pszKeyName)
120 {
121 delete [] m_pszKeyName;
122 m_pszKeyName = NULL;
123 }
124
125 if (m_pszMachineName)
126 {
127 delete [] m_pszMachineName;
128 m_pszMachineName = NULL;
129 }
130
131 LONG nError = ERROR_SUCCESS;
132 if((m_hKey != NULL)&&(!IsHive(m_hKey)))
133 nError = RegCloseKey(m_hKey);
134
135 m_hKey = NULL;
136
137 return (nError == ERROR_SUCCESS)?S_OK:E_FAIL;
138 }
139
IsHive(HKEY hKey)140 BOOL CRegistryKey::IsHive(HKEY hKey)
141 {
142 return ((hKey == HKEY_CLASSES_ROOT)||
143 (hKey == HKEY_CURRENT_USER)||
144 (hKey == HKEY_LOCAL_MACHINE)||
145 (hKey == HKEY_USERS)||
146 (hKey == HKEY_PERFORMANCE_DATA)||
147 (hKey == HKEY_CURRENT_CONFIG)||
148 (hKey == HKEY_DYN_DATA));
149 }
150
~CRegistryKey()151 CRegistryKey::~CRegistryKey()
152 {
153 Uninit();
154 }
155
GetKeyName()156 const TCHAR * CRegistryKey::GetKeyName()
157 {
158 return m_pszKeyName?m_pszKeyName:(m_pszMachineName?m_pszMachineName:_T("\\"));
159 }
160
IsRoot()161 BOOL CRegistryKey::IsRoot()
162 {
163 return m_hKey == NULL;
164 }
165
OpenSubkey(REGSAM samDesired,const TCHAR * pszSubkeyName,HKEY & rhKey)166 LONG CRegistryKey::OpenSubkey(REGSAM samDesired, const TCHAR *pszSubkeyName, HKEY &rhKey)
167 {
168 if (m_hKey == NULL)
169 { // subkey of root key is hive root key.
170 if ((_tcsicmp(pszSubkeyName,_T("HKCR")) == 0)||
171 (_tcsicmp(pszSubkeyName,_T("HKEY_CLASSES_ROOT")) == 0))
172 {
173 rhKey = HKEY_CLASSES_ROOT;
174
175 if (m_pszMachineName)
176 return ERROR_FILE_NOT_FOUND;
177
178 return ERROR_SUCCESS;
179 }
180 else if ((_tcsicmp(pszSubkeyName,_T("HKCU")) == 0)||
181 (_tcsicmp(pszSubkeyName,_T("HKEY_CURRENT_USER")) == 0))
182 {
183 rhKey = HKEY_CURRENT_USER;
184
185 if (m_pszMachineName)
186 return ERROR_FILE_NOT_FOUND;
187
188 return ERROR_SUCCESS;
189 }
190 else if ((_tcsicmp(pszSubkeyName,_T("HKLM")) == 0)||
191 (_tcsicmp(pszSubkeyName,_T("HKEY_LOCAL_MACHINE")) == 0))
192 {
193 rhKey = HKEY_LOCAL_MACHINE;
194
195 if (m_pszMachineName)
196 return RegConnectRegistry(m_pszMachineName,rhKey,&rhKey);
197
198 return ERROR_SUCCESS;
199 }
200 else if ((_tcsicmp(pszSubkeyName,_T("HKU")) == 0)||
201 (_tcsicmp(pszSubkeyName,_T("HKEY_USERS")) == 0))
202 {
203 rhKey = HKEY_USERS;
204
205 if (m_pszMachineName)
206 return RegConnectRegistry(m_pszMachineName,rhKey,&rhKey);
207
208 return ERROR_SUCCESS;
209 }
210 else if ((_tcsicmp(pszSubkeyName,_T("HKPD")) == 0)||
211 (_tcsicmp(pszSubkeyName,_T("HKEY_PERFORMANCE_DATA")) == 0))
212 {
213 rhKey = HKEY_PERFORMANCE_DATA;
214
215 if (m_pszMachineName)
216 return RegConnectRegistry(m_pszMachineName,rhKey,&rhKey);
217
218 return ERROR_SUCCESS;
219 }
220 else if ((_tcsicmp(pszSubkeyName,_T("HKDD")) == 0)||
221 (_tcsicmp(pszSubkeyName,_T("HKEY_DYN_DATA")) == 0))
222 {
223 rhKey = HKEY_DYN_DATA;
224
225 if (m_pszMachineName)
226 return RegConnectRegistry(m_pszMachineName,rhKey,&rhKey);
227
228 return ERROR_SUCCESS;
229 }
230 else if ((_tcsicmp(pszSubkeyName,_T("HKCC")) == 0)||
231 (_tcsicmp(pszSubkeyName,_T("HKEY_CURRENT_CONFIG")) == 0))
232 {
233 rhKey = HKEY_CURRENT_CONFIG;
234
235 if (m_pszMachineName)
236 {
237 TCHAR *pch = m_pszMachineName;
238 while (*pch)
239 pch++;
240 pch--;
241
242 ASSERT(*pch == _T('\\'));
243 if (*pch != _T('\\'))
244 return ERROR_INTERNAL_ERROR;
245
246 *pch = 0;
247
248 LONG nError = RegConnectRegistry(m_pszMachineName,rhKey,&rhKey);
249
250 *pch = _T('\\');
251
252 return nError;
253 }
254
255 return ERROR_SUCCESS;
256 }
257 else
258 {
259 return ERROR_FILE_NOT_FOUND;
260 }
261 }
262
263 return RegOpenKeyEx(m_hKey,pszSubkeyName,0,samDesired,&rhKey);
264 }
265
OpenSubkey(REGSAM samDesired,const TCHAR * pszSubkeyName,CRegistryKey & rKey)266 LONG CRegistryKey::OpenSubkey(REGSAM samDesired, const TCHAR *pszSubkeyName, CRegistryKey &rKey)
267 {
268 HKEY hKey;
269 LONG nError = OpenSubkey(samDesired, pszSubkeyName, hKey);
270
271 if (nError == ERROR_SUCCESS)
272 {
273 const TCHAR *pszKeyName = GetKeyName();
274 size_t size = _tcslen(pszKeyName) + _tcslen(pszSubkeyName) + 1;
275 TCHAR *pszSubkeyFullName = new (std::nothrow) TCHAR [size];
276 if (!pszSubkeyFullName)
277 {
278 nError = RegCloseKey(hKey);
279 ASSERT(nError == ERROR_SUCCESS);
280 return ERROR_OUTOFMEMORY;
281 }
282 _tcscpy(pszSubkeyFullName,pszKeyName);
283 _tcscat(pszSubkeyFullName,pszSubkeyName);
284 HRESULT hr = rKey.Init(hKey,GetKeyName(),pszSubkeyName,samDesired);
285 delete[] pszSubkeyName;
286 if (FAILED(hr))
287 {
288 nError = RegCloseKey(hKey);
289 ASSERT(nError == ERROR_SUCCESS);
290 if (hr == (HRESULT)E_OUTOFMEMORY)
291 return ERROR_OUTOFMEMORY;
292 else
293 return ERROR_INTERNAL_ERROR;
294 }
295 }
296
297 return nError;
298 }
299
GetSubkeyNameMaxLength(DWORD & rdwMaxSubkeyNameLength)300 LONG CRegistryKey::GetSubkeyNameMaxLength(DWORD &rdwMaxSubkeyNameLength)
301 {
302 if (m_hKey == NULL)
303 { // root key
304 rdwMaxSubkeyNameLength = 0;
305 for(int i = 0; i < 7 ; i++)
306 {
307 size_t l = _tcslen(g_ppszHiveNames[i]);
308 if (rdwMaxSubkeyNameLength < l)
309 rdwMaxSubkeyNameLength = l;
310 }
311
312 rdwMaxSubkeyNameLength++; // terminating null
313
314 return ERROR_SUCCESS;
315 }
316
317 LONG nRet;
318
319 nRet = RegQueryInfoKey(m_hKey,NULL,NULL,NULL,NULL,&rdwMaxSubkeyNameLength,NULL,NULL,NULL,NULL,NULL,NULL);
320
321 rdwMaxSubkeyNameLength = (nRet == ERROR_SUCCESS)?(rdwMaxSubkeyNameLength+1):0;
322
323 return nRet;
324 }
325
InitSubkeyEnumeration(TCHAR * pszSubkeyNameBuffer,DWORD dwBufferSize)326 void CRegistryKey::InitSubkeyEnumeration(TCHAR *pszSubkeyNameBuffer, DWORD dwBufferSize)
327 {
328 m_pchSubkeyNameBuffer = pszSubkeyNameBuffer;
329 m_dwSubkeyNameBufferSize = dwBufferSize;
330 m_dwCurrentSubKeyIndex = 0;
331 }
332
GetNextSubkeyName(DWORD * pdwActualSize)333 LONG CRegistryKey::GetNextSubkeyName(DWORD *pdwActualSize)
334 {
335 LONG nError;
336
337 if (m_hKey == NULL)
338 {
339 if (m_dwCurrentSubKeyIndex < (DWORD)(m_pszMachineName?5:7))
340 {
341 DWORD dwIndex = m_pszMachineName?m_dwCurrentSubKeyIndex+2:m_dwCurrentSubKeyIndex;
342 _tcsncpy(m_pchSubkeyNameBuffer,g_ppszHiveNames[dwIndex],m_dwSubkeyNameBufferSize);
343 nError = ERROR_SUCCESS;
344 if (pdwActualSize)
345 *pdwActualSize = strlen(m_pchSubkeyNameBuffer);
346 }
347 else
348 {
349 nError = ERROR_NO_MORE_ITEMS;
350 }
351 }
352 else
353 {
354 DWORD dwActualSize = m_dwSubkeyNameBufferSize;
355 FILETIME ft;
356 nError = RegEnumKeyEx(m_hKey,
357 m_dwCurrentSubKeyIndex,
358 m_pchSubkeyNameBuffer,
359 &dwActualSize,
360 NULL,
361 NULL,
362 NULL,
363 &ft);
364 if (pdwActualSize)
365 *pdwActualSize = dwActualSize;
366 }
367
368 m_dwCurrentSubKeyIndex++;
369
370 if (pdwActualSize)
371 *pdwActualSize = strlen(m_pchSubkeyNameBuffer);
372 return nError;
373 }
374
GetSubkeyCount(DWORD & rdwSubkeyCount)375 LONG CRegistryKey::GetSubkeyCount(DWORD &rdwSubkeyCount)
376 {
377 return RegQueryInfoKeyW(m_hKey,NULL,NULL,NULL,&rdwSubkeyCount,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
378 }
379
GetMaxValueDataSize(DWORD & rdwMaxValueDataBuferSize)380 LONG CRegistryKey::GetMaxValueDataSize(DWORD& rdwMaxValueDataBuferSize)
381 {
382 return RegQueryInfoKeyW(m_hKey,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&rdwMaxValueDataBuferSize,NULL,NULL);
383 }
384
GetMaxValueNameLength(DWORD & rdwMaxValueNameBuferSize)385 LONG CRegistryKey::GetMaxValueNameLength(DWORD& rdwMaxValueNameBuferSize)
386 {
387 rdwMaxValueNameBuferSize = 0;
388
389 if (!m_hKey)
390 return 0; // the root key abstraction has only subkeys (hives)
391
392 LONG nError = RegQueryInfoKeyW(m_hKey,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&rdwMaxValueNameBuferSize,NULL,NULL,NULL);
393
394 rdwMaxValueNameBuferSize++;
395 return nError;
396 }
397
InitValueEnumeration(TCHAR * pszValueNameBuffer,DWORD dwValueNameBufferSize,BYTE * pbValueDataBuffer,DWORD dwValueDataBufferSize,DWORD * pdwType)398 void CRegistryKey::InitValueEnumeration(TCHAR *pszValueNameBuffer,
399 DWORD dwValueNameBufferSize,
400 BYTE *pbValueDataBuffer,
401 DWORD dwValueDataBufferSize,
402 DWORD *pdwType)
403 {
404 m_pszValueNameBuffer = pszValueNameBuffer;
405 m_dwValueNameBufferSize = dwValueNameBufferSize;
406 m_pbValueDataBuffer = pbValueDataBuffer;
407 m_dwValueDataBufferSize = dwValueDataBufferSize;
408 m_pdwType = pdwType;
409
410 m_dwCurrentValueIndex = 0;
411 }
412
413
414 // On input dwValueNameSize is size in characters of buffer pointed by pchValueNameBuffer
415 // On output dwValueNameSize contains number of characters stored in buffer
GetNextValue(DWORD * pdwNameActualSize,DWORD * pdwDataActualSize)416 LONG CRegistryKey::GetNextValue(DWORD *pdwNameActualSize, DWORD *pdwDataActualSize)
417 {
418 if (!m_hKey)
419 return ERROR_NO_MORE_ITEMS; // the root key abstraction has only subkeys (hives)
420
421 DWORD dwValueNameBufferSize = m_dwValueNameBufferSize;
422 DWORD dwValueDataBufferSize = m_dwValueDataBufferSize;
423 LONG nError = RegEnumValue(m_hKey,
424 m_dwCurrentValueIndex,
425 m_pszValueNameBuffer,
426 &dwValueNameBufferSize,
427 NULL,
428 m_pdwType,
429 m_pbValueDataBuffer,
430 &dwValueDataBufferSize);
431
432 if (pdwNameActualSize)
433 *pdwNameActualSize = dwValueNameBufferSize;
434
435 if (pdwDataActualSize)
436 *pdwDataActualSize = dwValueDataBufferSize;
437
438 m_dwCurrentValueIndex++;
439 return nError;
440 }
441
GetValueCount(DWORD & rdwValueCount)442 LONG CRegistryKey::GetValueCount(DWORD& rdwValueCount)
443 {
444 if (!m_hKey)
445 return 0; // the root key abstraction has only subkeys (hives)
446
447 return RegQueryInfoKeyW(m_hKey,NULL,NULL,NULL,NULL,NULL,NULL,&rdwValueCount,NULL,NULL,NULL,NULL);
448 }
449
GetDefaultValue(DWORD * pdwType,BYTE * pbValueDataBuffer,DWORD dwValueDataBufferSize,DWORD * pdwValueDataActualSize)450 LONG CRegistryKey::GetDefaultValue(DWORD *pdwType,
451 BYTE *pbValueDataBuffer,
452 DWORD dwValueDataBufferSize,
453 DWORD *pdwValueDataActualSize)
454 {
455 DWORD dwBufferSize = dwValueDataBufferSize;
456
457 LONG nError = RegQueryValueEx(m_hKey,NULL,NULL,pdwType,pbValueDataBuffer,&dwBufferSize);
458
459 if (pdwValueDataActualSize && (nError == ERROR_SUCCESS))
460 *pdwValueDataActualSize = dwBufferSize;
461
462 return nError;
463 }
464
GetValueTypeName(DWORD dwType)465 const TCHAR * CRegistryKey::GetValueTypeName(DWORD dwType)
466 {
467 switch(dwType)
468 {
469 case REG_NONE:
470 return _T("REG_NONE");
471 case REG_SZ:
472 return _T("REG_SZ");
473 case REG_EXPAND_SZ:
474 return _T("REG_EXPAND_SZ");
475 case REG_BINARY:
476 return _T("REG_BINARY");
477 case REG_DWORD_LITTLE_ENDIAN:
478 return _T("REG_DWORD_LITTLE_ENDIAN");
479 case REG_DWORD_BIG_ENDIAN:
480 return _T("REG_DWORD_BIG_ENDIAN");
481 case REG_LINK:
482 return _T("REG_LINK");
483 case REG_MULTI_SZ:
484 return _T("REG_MULTI_SZ");
485 case REG_RESOURCE_LIST:
486 return _T("REG_RESOURCE_LIST");
487 case REG_FULL_RESOURCE_DESCRIPTOR:
488 return _T("REG_FULL_RESOURCE_DESCRIPTOR");
489 case REG_RESOURCE_REQUIREMENTS_LIST:
490 return _T("REG_RESOURCE_REQUIREMENTS_LIST");
491 default:
492 return _T("Unknown Type");
493 }
494 }
495
GetValue(TCHAR * pchValueName,DWORD * pdwType,LPBYTE lpValueDataBuffer,DWORD * pdwValueDataSize)496 DWORD CRegistryKey::GetValue(TCHAR *pchValueName, DWORD *pdwType, LPBYTE lpValueDataBuffer, DWORD *pdwValueDataSize)
497 {
498 return RegQueryValueEx(m_hKey,pchValueName,NULL,pdwType,lpValueDataBuffer,pdwValueDataSize);
499 }
500
CreateSubkey(REGSAM samDesired,const TCHAR * pszSubkeyName,HKEY & rhKey,BOOL * pblnOpened,BOOL blnVolatile)501 LONG CRegistryKey::CreateSubkey(REGSAM samDesired,
502 const TCHAR *pszSubkeyName,
503 HKEY &rhKey,
504 BOOL *pblnOpened,
505 BOOL blnVolatile)
506 {
507 DWORD dwDisposition;
508
509 LONG nError = RegCreateKeyEx(
510 m_hKey,
511 pszSubkeyName,
512 0,
513 NULL,
514 blnVolatile?REG_OPTION_VOLATILE:REG_OPTION_NON_VOLATILE,
515 samDesired,
516 NULL,
517 &rhKey,
518 &dwDisposition);
519
520 if ((nError == ERROR_SUCCESS)&&(pblnOpened))
521 *pblnOpened = dwDisposition == REG_OPENED_EXISTING_KEY;
522
523 return nError;
524 }
525
GetLastWriteTime(SYSTEMTIME & st)526 LONG CRegistryKey::GetLastWriteTime(SYSTEMTIME &st)
527 {
528 FILETIME ftLocal,ft;
529 LONG nError = RegQueryInfoKeyW(m_hKey,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&ft);
530
531 if (nError == ERROR_SUCCESS)
532 {
533 FileTimeToLocalFileTime(&ft,&ftLocal);
534 FileTimeToSystemTime(&ftLocal,&st);
535 }
536
537 return nError;
538 }
539
GetLastWriteTime()540 const TCHAR * CRegistryKey::GetLastWriteTime()
541 {
542 SYSTEMTIME st;
543 if (GetLastWriteTime(st) != ERROR_SUCCESS)
544 return _T("(Cannot get time last write time)");
545
546 static TCHAR Buffer[256];
547 _stprintf(Buffer,_T("%d.%d.%d %02d:%02d:%02d"),st.wDay,st.wMonth,st.wYear,st.wHour,st.wMinute,st.wSecond);
548 return Buffer;
549 }
550
GetSecurityDescriptorLength(DWORD * pdwSecurityDescriptor)551 LONG CRegistryKey::GetSecurityDescriptorLength(DWORD *pdwSecurityDescriptor)
552 {
553 return RegQueryInfoKeyW(m_hKey,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
554 NULL,NULL,pdwSecurityDescriptor,NULL);
555 }
556
GetSecurityDescriptor(SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor,LPDWORD lpcbSecurityDescriptor)557 LONG CRegistryKey::GetSecurityDescriptor(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, LPDWORD lpcbSecurityDescriptor)
558 {
559 return RegGetKeySecurity(m_hKey,SecurityInformation,pSecurityDescriptor,lpcbSecurityDescriptor);
560 }
561
DeleteSubkey(const TCHAR * pszSubkeyName)562 LONG CRegistryKey::DeleteSubkey(const TCHAR *pszSubkeyName)
563 {
564 return RegDeleteKey(m_hKey,pszSubkeyName);
565 }
566
SetValue(LPCTSTR pszValueName,DWORD dwType,BYTE * lpData,DWORD dwDataSize)567 LONG CRegistryKey::SetValue(LPCTSTR pszValueName, DWORD dwType, BYTE *lpData, DWORD dwDataSize)
568 {
569 return RegSetValueEx(m_hKey,pszValueName,0,dwType,lpData,dwDataSize);
570 }
571
DeleteValue(const TCHAR * pszValueName)572 LONG CRegistryKey::DeleteValue(const TCHAR *pszValueName)
573 {
574 return RegDeleteValue(m_hKey,pszValueName);
575 }
576