Remove always defined FEATURE_CORECLR
[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         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
118 BOOL 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 //*****************************************************************************
131 DWORD 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
165 HRESULT 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
173 ULONGLONG 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.
197 HRESULT 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
331 LPWSTR 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 //*****************************************************************************
491 void 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 //*****************************************************************************
502 DWORD 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 //
531 class ProbabilisticNameSet {
532 public:
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
572 private:
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 //
605 static const unsigned cchRegValueNameMax = 80;
606
607 BOOL REGUTIL::s_fUseRegCache = FALSE;
608 BOOL REGUTIL::s_fUseEnvCache = FALSE;
609 HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
610 HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
611 BOOL REGUTIL::s_fUseRegistry = TRUE;
612 static 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.
615 static 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 //
646 void 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
785 failure:
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 //
798 BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name)
799 {
800     WRAPPER_NO_CONTRACT;
801
802     return !s_fUseRegCache
803            || (wcslen(name) > cchRegValueNameMax)
804            || regNames.MayContain(name);
805 }
806
807 BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name)
808 {
809     WRAPPER_NO_CONTRACT;
810
811     return !s_fUseEnvCache
812            || envNames.MayContain(name);
813 }
814
815 #endif // ALLOW_REGISTRY