1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// regutil.cpp
6//
7
8//
9// This module contains a set of functions that can be used to access the
10// registry.
11//
12//*****************************************************************************
13
14
15#include "stdafx.h"
16#include "utilcode.h"
17#include "mscoree.h"
18#include "sstring.h"
19#include "ex.h"
20
21#define COMPLUS_PREFIX W("COMPlus_")
22#define LEN_OF_COMPLUS_PREFIX 8
23
24#if (!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(DEBUG)) && !defined(FEATURE_PAL)
25#define ALLOW_REGISTRY
26#endif
27
28#undef WszRegCreateKeyEx
29#undef WszRegOpenKeyEx
30#undef WszRegOpenKey
31#define WszRegCreateKeyEx RegCreateKeyExW
32#define WszRegOpenKeyEx RegOpenKeyExW
33#define WszRegOpenKey(hKey, wszSubKey, phkRes) RegOpenKeyExW(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes)
34
35//*****************************************************************************
36// Reads from the environment setting
37//*****************************************************************************
38LPWSTR REGUTIL::EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS)
39{
40 CONTRACTL
41 {
42 NOTHROW;
43 GC_NOTRIGGER;
44 FORBID_FAULT;
45 SO_TOLERANT;
46 CANNOT_TAKE_LOCK;
47 }
48 CONTRACTL_END;
49
50 WCHAR buff[64];
51
52 if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0)))
53 {
54 return NULL;
55 }
56
57#ifdef ALLOW_REGISTRY
58 if (fPrependCOMPLUS)
59 {
60 if (!EnvCacheValueNameSeenPerhaps(name))
61 return NULL;
62 }
63#endif // ALLOW_REGISTRY
64
65 if (fPrependCOMPLUS)
66 {
67 wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX);
68 }
69 else
70 {
71 *buff = 0;
72 }
73
74 wcscat_s(buff, _countof(buff), name);
75
76 FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
77
78
79 NewArrayHolder<WCHAR> ret = NULL;
80 HRESULT hr = S_OK;
81 DWORD Len;
82 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(SetLastError(COR_E_STACKOVERFLOW); return NULL;)
83 EX_TRY
84 {
85 PathString temp;
86
87 Len = WszGetEnvironmentVariable(buff, temp);
88 if (Len != 0)
89 {
90 ret = temp.GetCopyOfUnicodeString();
91 }
92
93 }
94 EX_CATCH_HRESULT(hr);
95 END_SO_INTOLERANT_CODE
96
97 if (hr != S_OK)
98 {
99 SetLastError(hr);
100 }
101
102 if(ret != NULL)
103 {
104 return ret.Extract();
105 }
106
107 return NULL;
108
109
110}
111
112#ifdef ALLOW_REGISTRY
113
114
115#endif // ALLOW_REGISTRY
116
117
118BOOL REGUTIL::UseRegistry()
119{
120#if !defined(ALLOW_REGISTRY)
121 return TRUE;
122#else
123 return s_fUseRegistry;
124#endif
125}// UseRegistry
126
127//*****************************************************************************
128// Reads a DWORD from the COR configuration according to the level specified
129// Returns back defValue if the key cannot be found
130//*****************************************************************************
131DWORD REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, CORConfigLevel level, BOOL fPrependCOMPLUS)
132{
133 CONTRACTL
134 {
135 NOTHROW;
136 GC_NOTRIGGER;
137 FORBID_FAULT;
138 SO_TOLERANT;
139 CANNOT_TAKE_LOCK;
140 }
141 CONTRACTL_END;
142
143 SUPPORTS_DAC_HOST_ONLY;
144
145 ULONGLONG result;
146 GetConfigInteger(name, defValue, &result, TRUE, level, fPrependCOMPLUS);
147
148 return (DWORD)result;
149}
150
151#define uniwcst(val, endptr, base) (fGetDWORD ? wcstoul(val, endptr, base) : _wcstoui64(val, endptr, base))
152
153//
154// Look up a dword config value, and write the result to the DWORD passed in by reference.
155//
156// Return value:
157// * E_FAIL if the value is not found. (result is assigned the default value)
158// * S_OK if the value is found. (result is assigned the value that was found)
159//
160// Arguments:
161// * info - see file:../inc/CLRConfig.h for details
162// * result - Pointer to the output DWORD.
163//
164// static
165HRESULT REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, __out DWORD * result, CORConfigLevel level, BOOL fPrependCOMPLUS)
166{
167 ULONGLONG ullResult;
168 HRESULT hr = GetConfigInteger(name, defValue, &ullResult, TRUE, level, fPrependCOMPLUS);
169 *result = (DWORD)ullResult;
170 return hr;
171}
172
173ULONGLONG REGUTIL::GetConfigULONGLONG_DontUse_(LPCWSTR name, ULONGLONG defValue, CORConfigLevel level, BOOL fPrependCOMPLUS)
174{
175 CONTRACTL
176 {
177 NOTHROW;
178 GC_NOTRIGGER;
179 FORBID_FAULT;
180 SO_TOLERANT;
181 CANNOT_TAKE_LOCK;
182 }
183 CONTRACTL_END;
184
185 SUPPORTS_DAC_HOST_ONLY;
186
187 ULONGLONG result;
188 GetConfigInteger(name, defValue, &result, FALSE, level, fPrependCOMPLUS);
189
190 return result;
191}
192
193// This function should really be refactored to return the string from the environment and let the caller decide
194// what to convert it to; and return the buffer read from the reg call.
195// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as
196// a 32-bit number. When we have the _wcstoui64 API on MAC we will use uniwcst instead of wcstoul.
197HRESULT REGUTIL::GetConfigInteger(LPCWSTR name, ULONGLONG defValue, __out ULONGLONG * result, BOOL fGetDWORD, CORConfigLevel level, BOOL fPrependCOMPLUS)
198{
199 CONTRACTL
200 {
201 NOTHROW;
202 GC_NOTRIGGER;
203 FORBID_FAULT;
204 SO_TOLERANT;
205 CANNOT_TAKE_LOCK;
206 }
207 CONTRACTL_END;
208
209 SUPPORTS_DAC_HOST_ONLY;
210
211 ULONGLONG rtn;
212 ULONGLONG ret = 0;
213 DWORD type = 0;
214 HKEY userKey;
215 HKEY machineKey;
216 DWORD size = 4;
217
218 FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
219
220 if (level & COR_CONFIG_ENV)
221 {
222 WCHAR* val = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first
223 if (val != 0) {
224 errno = 0;
225 LPWSTR endPtr;
226 rtn = uniwcst(val, &endPtr, 16); // treat it has hex
227 BOOL fSuccess = ((errno != ERANGE) && (endPtr != val));
228 delete[] val;
229
230 if (fSuccess) // success
231 {
232 *result = rtn;
233 return (S_OK);
234 }
235 }
236 }
237
238 // Early out if no registry access, simplifies following code.
239 //
240 if (!UseRegistry() || !(level & COR_CONFIG_REGISTRY))
241 {
242 *result = defValue;
243 return (E_FAIL);
244 }
245
246#ifdef ALLOW_REGISTRY
247 // Probe the config cache to see if there is any point
248 // probing the registry; if not, don't bother.
249 //
250 if (!RegCacheValueNameSeenPerhaps(name))
251 {
252 *result = defValue;
253 return (E_FAIL);
254 }
255#endif // ALLOW_REGISTRY
256
257 if (level & COR_CONFIG_USER)
258 {
259#ifdef ALLOW_REGISTRY
260 {
261 LONG retVal = ERROR_SUCCESS;
262 BOOL bCloseHandle = FALSE;
263 userKey = s_hUserFrameworkKey;
264
265 if (userKey == INVALID_HANDLE_VALUE)
266 {
267 retVal = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey);
268 bCloseHandle = TRUE;
269 }
270
271 if (retVal == ERROR_SUCCESS)
272 {
273 rtn = WszRegQueryValueEx(userKey, name, 0, &type, (LPBYTE)&ret, &size);
274
275 if (bCloseHandle)
276 VERIFY(!RegCloseKey(userKey));
277
278 if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD)))
279 {
280 *result = ret;
281 return (S_OK);
282 }
283 }
284 }
285#endif // ALLOW_REGISTRY
286 }
287
288 if (level & COR_CONFIG_MACHINE)
289 {
290#ifdef ALLOW_REGISTRY
291 {
292 LONG retVal = ERROR_SUCCESS;
293 BOOL bCloseHandle = FALSE;
294 machineKey = s_hMachineFrameworkKey;
295
296 if (machineKey == INVALID_HANDLE_VALUE)
297 {
298 retVal = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey);
299 bCloseHandle = TRUE;
300 }
301
302 if (retVal == ERROR_SUCCESS)
303 {
304 rtn = WszRegQueryValueEx(machineKey, name, 0, &type, (LPBYTE)&ret, &size);
305
306 if (bCloseHandle)
307 VERIFY(!RegCloseKey(machineKey));
308
309 if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD)))
310 {
311 *result = ret;
312 return (S_OK);
313 }
314 }
315 }
316#endif // ALLOW_REGISTRY
317 }
318
319 *result = defValue;
320 return (E_FAIL);
321}
322
323#define FUSION_REGISTRY_KEY_W W("Software\\Microsoft\\Fusion")
324
325//*****************************************************************************
326// Reads a string from the COR configuration according to the level specified
327// The caller is responsible for deallocating the returned string by
328// calling code:REGUTIL::FreeConfigString or using a code:ConfigStringHolder
329//*****************************************************************************
330
331LPWSTR REGUTIL::GetConfigString_DontUse_(LPCWSTR name, BOOL fPrependCOMPLUS, CORConfigLevel level, BOOL fUsePerfCache)
332{
333 CONTRACTL
334 {
335 NOTHROW;
336 GC_NOTRIGGER;
337 FORBID_FAULT;
338 }
339 CONTRACTL_END;
340
341#ifdef ALLOW_REGISTRY
342 HRESULT lResult;
343 RegKeyHolder userKey = NULL;
344 RegKeyHolder machineKey = NULL;
345 RegKeyHolder fusionKey = NULL;
346 DWORD type;
347 DWORD size;
348#endif // ALLOW_REGISTRY
349 NewArrayHolder<WCHAR> ret(NULL);
350
351 FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
352
353 if (level & COR_CONFIG_ENV)
354 {
355 ret = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first
356 if (ret != 0) {
357 if (*ret != 0)
358 {
359 ret.SuppressRelease();
360 return(ret);
361 }
362 ret.Clear();
363 }
364 }
365
366 // Early out if no registry access, simplifies following code.
367 //
368 if (!UseRegistry() || !(level & COR_CONFIG_REGISTRY))
369 {
370 return(ret);
371 }
372
373#ifdef ALLOW_REGISTRY
374 // Probe the config cache to see if there is any point
375 // probing the registry; if not, don't bother.
376 //
377 if (fUsePerfCache && !RegCacheValueNameSeenPerhaps(name))
378 return ret;
379#endif // ALLOW_REGISTRY
380
381 if (level & COR_CONFIG_USER)
382 {
383#ifdef ALLOW_REGISTRY
384 BOOL bUsingCachedKey = FALSE;
385
386 if (s_hUserFrameworkKey != INVALID_HANDLE_VALUE)
387 {
388 bUsingCachedKey = TRUE;
389 userKey = s_hUserFrameworkKey;
390 }
391
392 if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey) == ERROR_SUCCESS)
393 {
394 BOOL bReturn = FALSE;
395 if (WszRegQueryValueEx(userKey, name, 0, &type, 0, &size) == ERROR_SUCCESS &&
396 type == REG_SZ)
397 {
398 ret = (LPWSTR) new (nothrow) BYTE [size];
399 if (ret)
400 {
401 ret[0] = W('\0');
402 lResult = WszRegQueryValueEx(userKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
403 _ASSERTE(lResult == ERROR_SUCCESS);
404 {
405 ret.SuppressRelease();
406 }
407 }
408 bReturn = TRUE;
409 }
410
411 if (bUsingCachedKey)
412 userKey.SuppressRelease();
413
414 if (bReturn)
415 return ret;
416 }
417
418#endif // ALLOW_REGISTRY
419 }
420
421 if (level & COR_CONFIG_MACHINE)
422 {
423#ifdef ALLOW_REGISTRY
424 BOOL bUsingCachedKey = FALSE;
425
426 if (s_hMachineFrameworkKey != INVALID_HANDLE_VALUE)
427 {
428 bUsingCachedKey = TRUE;
429 machineKey = s_hMachineFrameworkKey;
430 }
431
432 if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey) == ERROR_SUCCESS)
433 {
434 BOOL bReturn = FALSE;
435 if (WszRegQueryValueEx(machineKey, name, 0, &type, 0, &size) == ERROR_SUCCESS &&
436 type == REG_SZ)
437 {
438 ret = (LPWSTR) new (nothrow) BYTE [size];
439 if (ret)
440 {
441 ret[0] = W('\0');
442 lResult = WszRegQueryValueEx(machineKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
443 _ASSERTE(lResult == ERROR_SUCCESS);
444 {
445 ret.SuppressRelease();
446 }
447 }
448 bReturn = TRUE;
449 }
450
451 if (bUsingCachedKey)
452 machineKey.SuppressRelease();
453
454 if (bReturn)
455 return ret;
456 }
457
458#endif // ALLOW_REGISTRY
459 }
460
461 if (level & COR_CONFIG_FUSION)
462 {
463#ifdef ALLOW_REGISTRY
464 if ((WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FUSION_REGISTRY_KEY_W, 0, KEY_READ, &fusionKey) == ERROR_SUCCESS) &&
465 (WszRegQueryValueEx(fusionKey, name, 0, &type, 0, &size) == ERROR_SUCCESS) &&
466 type == REG_SZ)
467 {
468 ret = (LPWSTR) new (nothrow) BYTE [size];
469 if (!ret)
470 {
471 return NULL;
472 }
473 ret[0] = W('\0');
474 lResult = WszRegQueryValueEx(fusionKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
475 _ASSERTE(lResult == ERROR_SUCCESS);
476 ret.SuppressRelease();
477 return(ret);
478 }
479#endif // ALLOW_REGISTRY
480 }
481
482 return NULL;
483}
484
485//*****************************************************************************
486// Deallocation function for code:REGUTIL::GetConfigString_DontUse_
487//
488// Notes:
489// Use a code:ConfigStringHolder to automatically call this.
490//*****************************************************************************
491void REGUTIL::FreeConfigString(__in_z LPWSTR str)
492{
493 LIMITED_METHOD_CONTRACT;
494
495 delete [] str;
496}
497
498//*****************************************************************************
499// Reads a BIT flag from the COR configuration according to the level specified
500// Returns back defValue if the key cannot be found
501//*****************************************************************************
502DWORD REGUTIL::GetConfigFlag_DontUse_(LPCWSTR name, DWORD bitToSet, BOOL defValue)
503{
504 WRAPPER_NO_CONTRACT;
505
506 return(GetConfigDWORD_DontUse_(name, defValue) != 0 ? bitToSet : 0);
507}
508
509
510#ifdef ALLOW_REGISTRY
511
512
513
514//
515// ProbabilisticNameSet:
516//
517// (Used by ConfigCache, below. If used elsewhere, might justify
518// promotion to a standalone header file.)
519//
520// Represent a set of names in a small, fixed amount of storage.
521// We turn a name into a small integer, then add the integer to a bitvector.
522// An old trick we used in VC++4 minimal rebuild.
523//
524// For best results, the number of elements should be a fraction of
525// the total number of bits in 'bits'.
526//
527// Note, only the const methods are thread-safe.
528// Callers are responsible for providing their own synchronization when
529// constructing and Add'ing names to the set.
530//
531class ProbabilisticNameSet {
532public:
533 ProbabilisticNameSet()
534 {
535 WRAPPER_NO_CONTRACT;
536
537 memset(bits, 0, sizeof(bits));
538 }
539
540 // Add a name to the set.
541 //
542 void Add(LPCWSTR name)
543 {
544 WRAPPER_NO_CONTRACT;
545
546 unsigned i, mask;
547 GetBitIndex(name, 0, &i, &mask);
548 bits[i] |= mask;
549 }
550
551 void Add(LPCWSTR name, DWORD count)
552 {
553 WRAPPER_NO_CONTRACT;
554
555 unsigned i, mask;
556 GetBitIndex(name, count, &i, &mask);
557 bits[i] |= mask;
558 }
559
560 // Return TRUE if a name *may have* been added to the set;
561 // return FALSE if the name *definitely* was NOT ever added to the set.
562 //
563 BOOL MayContain(LPCWSTR name) const
564 {
565 WRAPPER_NO_CONTRACT;
566
567 unsigned i, mask;
568 GetBitIndex(name, 0, &i, &mask);
569 return !!(bits[i] & mask);
570 }
571
572private:
573 static const unsigned cbitSet = 256U;
574 static const unsigned cbitWord = 8U*sizeof(unsigned);
575 unsigned bits[cbitSet/cbitWord];
576
577 // Return the word index and bit mask corresponding to the bitvector member
578 // addressed by the *case-insensitive* hash of the given name.
579 //
580 void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const
581 {
582 LIMITED_METHOD_CONTRACT;
583 unsigned hash;
584 if (count > 0)
585 hash = HashiStringNKnownLower80(name, count) % cbitSet;
586 else
587 hash = HashiStringKnownLower80(name) % cbitSet;
588 *pi = hash / cbitWord;
589 *pmask = (1U << (hash % cbitWord));
590 }
591
592};
593
594
595// From the Win32 SDK docs:
596// Registry Element Size Limits
597// ...
598// The maximum size of a value name is as follows:
599// Windows Server 2003 and Windows XP: 16,383 characters
600// Windows 2000: 260 ANSI characters or 16,383 Unicode characters.
601// Windows Me/98/95: 255 characters
602// Despite that, we only cache value names of 80 characters or less --
603// longer names don't make sense as configuration settings names.
604//
605static const unsigned cchRegValueNameMax = 80;
606
607BOOL REGUTIL::s_fUseRegCache = FALSE;
608BOOL REGUTIL::s_fUseEnvCache = FALSE;
609HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
610HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
611BOOL REGUTIL::s_fUseRegistry = TRUE;
612static ProbabilisticNameSet regNames; // set of registry value names seen; should be
613 // a static field of REGUTIL but I don't
614 // want to expose ProbabilisticNameSet.
615static ProbabilisticNameSet envNames; // set of environment value names seen;
616
617// "Registry Configuration Cache"
618//
619// Initialize the (optional) registry config cache.
620//
621// The purpose of the cache is to avoid hundreds of registry probes
622// otherwise incurred by calls to GetConfigDWORD_DontUse_ and GetConfigString_DontUse_.
623//
624// We accomplish this by enumerating the relevant registry keys and
625// remembering the extant value names; and then by avoiding probing
626// for a name that was not seen in the enumeration (initialization) phase.
627//
628// It is optional in the sense that REGUTIL facilities like
629// GetConfigDWORD_DontUse_ and GetConfigString_DontUse_ will work fine if the cache
630// is never initialized; however, each config access then will hit
631// the registry (typically multiple times to search HKCU and HKLM).
632//
633//
634// Initialization: Enumerate these registry keys
635// HKCU Software\Microsoft\.NetFramework
636// HKLM Software\Microsoft\.NetFramework
637// for value names, and "remember" them in the ProbalisticNameSet 'names'.
638//
639// If we ever find a reg value named DisableConfigCache under any of these
640// three keys, the feature is disabled.
641//
642// This method is not thread-safe. It should only be called once.
643//
644// Perf Optimization for VSWhidbey:113373.
645//
646void REGUTIL::InitOptionalConfigCache()
647{
648 CONTRACTL
649 {
650 NOTHROW;
651 GC_NOTRIGGER;
652 }
653 CONTRACTL_END;
654
655 static const HKEY roots[] = { HKEY_CURRENT_USER,
656 HKEY_LOCAL_MACHINE};
657
658 LONG l = ERROR_SUCCESS; // general Win32 API error return code
659 HKEY hkey = NULL;
660
661 // No caching if the environment variable COMPlus_DisableConfigCache is set
662 //
663 if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0)
664 goto failure;
665
666 // Enumerate each root
667 //
668 for (int i = 0; i < NumItems(roots); i++) {
669 hkey = NULL; // defensive
670 l = WszRegOpenKeyEx(roots[i], FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hkey);
671 if (l == ERROR_FILE_NOT_FOUND) {
672 // That registry key is not present.
673 // For example, installation with no HKCU\...\.NETFramework.
674 // Should be OK to proceed.
675 continue;
676 }
677#if defined(FEATURE_APPX) && !defined(DACCESS_COMPILE)
678 else if (l == ERROR_ACCESS_DENIED && AppX::IsAppXProcess()) {
679 // If we encounter access denied for the current key in AppX, ignore
680 // the failure and continue to cache the rest. Effectively this means
681 // we are caching that key as containing no values, which is correct
682 // because in the unlikely event there are values hiding underneath
683 // later attempts to access them (open the key) would also hit access
684 // denied and continue on probing other locations.
685 continue;
686 }
687#endif // FEATURE_APPX && !DACCESS_COMPILE
688 else if (l != ERROR_SUCCESS) {
689 // Something else went wrong. To be safe, don't enable the cache.
690 goto failure;
691 }
692
693 // Enumerate every value name under this key.
694 //
695 for (int j = 0; ; j++) {
696 WCHAR wszValue[cchRegValueNameMax + 1];
697 DWORD dwValueSize = NumItems(wszValue);
698 l = WszRegEnumValue(hkey, j, wszValue, &dwValueSize,
699 NULL, NULL, NULL, NULL);
700
701 if (l == ERROR_SUCCESS) {
702 // Add value name to the names cache.
703 regNames.Add(wszValue);
704 }
705 else if (l == ERROR_NO_MORE_ITEMS) {
706 // Expected case: we've considered every value under this key.
707 break;
708 }
709 else if ((l == ERROR_INSUFFICIENT_BUFFER || l == ERROR_MORE_DATA) &&
710 (dwValueSize > cchRegValueNameMax)) {
711 // Name is too long. That's OK, we don't cache such names.
712 continue;
713 }
714#if defined(FEATURE_APPX) && !defined DACCESS_COMPILE
715 else if (l == ERROR_ACCESS_DENIED && AppX::IsAppXProcess()) {
716 // As above, ignore access denied in AppX and continue on trying to cache
717 continue;
718 }
719#endif // FEATURE_APPX && !DACCESS_COMPILE
720 else {
721 // WszRegEnumValue failed OOM, or something else went wrong.
722 // To be safe, don't enable the cache.
723 goto failure;
724 }
725 }
726
727 // Save the handles to framework regkeys so that future reads dont have to
728 // open it again
729 if (roots[i] == HKEY_CURRENT_USER)
730 s_hUserFrameworkKey = hkey;
731 else if (roots[i] == HKEY_LOCAL_MACHINE)
732 s_hMachineFrameworkKey = hkey;
733 else
734 RegCloseKey(hkey);
735
736 hkey = NULL;
737 }
738
739 // Success. We've enumerated all value names under the roots;
740 // enable the REGUTIL value name config cache.
741 //
742 s_fUseRegCache = TRUE;
743
744 // Now create a cache of environment variables
745 if (WCHAR * wszStrings = WszGetEnvironmentStrings())
746 {
747 // GetEnvironmentStrings returns pointer to a null terminated block containing
748 // null terminated strings
749 for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++)
750 {
751 WCHAR wch = towlower(*wszCurr);
752
753 // Lets only cache env variables with the COMPlus prefix only
754 if (wch == W('c'))
755 {
756 WCHAR *wszName = wszCurr;
757
758 // Look for the separator between name and value
759 while (*wszCurr && *wszCurr != W('='))
760 wszCurr++;
761
762 if (*wszCurr == W('='))
763 {
764 // Check the prefix
765 if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX))
766 {
767 wszName += LEN_OF_COMPLUS_PREFIX;
768 envNames.Add(wszName, (DWORD) (wszCurr - wszName));
769 }
770 }
771
772 }
773 // Look for current string termination
774 while (*wszCurr)
775 wszCurr++;
776
777 }
778
779 WszFreeEnvironmentStrings(wszStrings);
780 s_fUseEnvCache = TRUE;
781
782 }
783 return;
784
785failure:
786 if (hkey != NULL)
787 RegCloseKey(hkey);
788}
789
790// Return TRUE if the registry value name was seen (or might have been seen)
791// in the registry at cache initialization time;
792// return FALSE if it definitely was not seen at startup.
793//
794// If not using the config cache, return TRUE always.
795//
796// Perf Optimization for VSWhidbey:113373.
797//
798BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name)
799{
800 WRAPPER_NO_CONTRACT;
801
802 return !s_fUseRegCache
803 || (wcslen(name) > cchRegValueNameMax)
804 || regNames.MayContain(name);
805}
806
807BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name)
808{
809 WRAPPER_NO_CONTRACT;
810
811 return !s_fUseEnvCache
812 || envNames.MayContain(name);
813}
814
815#endif // ALLOW_REGISTRY
816