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