Delete FriendAccessAllowedAttribute and associated dead code (#15101)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Resources / ResourceManager.cs
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 /*============================================================
6 **
7 ** 
8 ** 
9 **
10 **
11 ** Purpose: Default way to access String and Object resources
12 ** from an assembly.
13 **
14 ** 
15 ===========================================================*/
16
17 namespace System.Resources
18 {
19     using System;
20     using System.IO;
21     using System.Globalization;
22     using System.Collections;
23     using System.Text;
24     using System.Reflection;
25     using System.Runtime.Serialization;
26     using System.Security;
27     using System.Threading;
28     using System.Runtime.InteropServices;
29     using System.Runtime.CompilerServices;
30     using Microsoft.Win32;
31     using System.Collections.Generic;
32     using System.Runtime.Versioning;
33     using System.Diagnostics;
34
35 #if FEATURE_APPX
36     //
37     // This is implemented in System.Runtime.WindowsRuntime as function System.Resources.WindowsRuntimeResourceManager,
38     // allowing us to ask for a WinRT-specific ResourceManager.
39     // It is important to have WindowsRuntimeResourceManagerBase as regular class with virtual methods and default implementations. 
40     // Defining WindowsRuntimeResourceManagerBase as abstract class or interface will cause issues when adding more methods to it 
41     // because it'll create dependency between mscorlib and System.Runtime.WindowsRuntime which will require always shipping both DLLs together. 
42     //
43     // [FriendAccessAllowed]
44     internal abstract class WindowsRuntimeResourceManagerBase
45     {
46         public abstract bool Initialize(string libpath, string reswFilename, out PRIExceptionInfo exceptionInfo);
47
48         public abstract String GetString(String stringName, String startingCulture, String neutralResourcesCulture);
49
50         public abstract CultureInfo GlobalResourceContextBestFitCultureInfo
51         {
52             get;
53         }
54
55         public abstract bool SetGlobalResourceContextDefaultCulture(CultureInfo ci);
56     }
57
58     // [FriendAccessAllowed]
59     internal class PRIExceptionInfo
60     {
61         public string _PackageSimpleName;
62         public string _ResWFile;
63     }
64 #endif // FEATURE_APPX
65
66     // Resource Manager exposes an assembly's resources to an application for
67     // the correct CultureInfo.  An example would be localizing text for a 
68     // user-visible message.  Create a set of resource files listing a name 
69     // for a message and its value, compile them using ResGen, put them in
70     // an appropriate place (your assembly manifest(?)), then create a Resource 
71     // Manager and query for the name of the message you want.  The Resource
72     // Manager will use CultureInfo.GetCurrentUICulture() to look
73     // up a resource for your user's locale settings.
74     // 
75     // Users should ideally create a resource file for every culture, or
76     // at least a meaningful subset.  The filenames will follow the naming 
77     // scheme:
78     // 
79     // basename.culture name.resources
80     // 
81     // The base name can be the name of your application, or depending on 
82     // the granularity desired, possibly the name of each class.  The culture 
83     // name is determined from CultureInfo's Name property.  
84     // An example file name may be MyApp.en-US.resources for
85     // MyApp's US English resources.
86     // 
87     // -----------------
88     // Refactoring Notes
89     // -----------------
90     // In Feb 08, began first step of refactoring ResourceManager to improve
91     // maintainability (sd changelist 3012100). This resulted in breaking
92     // apart the InternalGetResourceSet "big loop" so that the file-based
93     // and manifest-based lookup was located in separate methods. 
94     // In Apr 08, continued refactoring so that file-based and manifest-based
95     // concerns are encapsulated by separate classes. At construction, the
96     // ResourceManager creates one of these classes based on whether the 
97     // RM will need to use file-based or manifest-based resources, and 
98     // afterwards refers to this through the interface IResourceGroveler.
99     // 
100     // Serialization Compat: Ideally, we could have refactored further but
101     // this would have broken serialization compat. For example, the
102     // ResourceManager member UseManifest and UseSatelliteAssem are no 
103     // longer relevant on ResourceManager. Similarly, other members could
104     // ideally be moved to the file-based or manifest-based classes 
105     // because they are only relevant for those types of lookup.
106     //
107     // Solution now / in the future: 
108     // For now, we simply use a mediator class so that we can keep these
109     // members on ResourceManager but allow the file-based and manifest-
110     // based classes to access/set these members in a uniform way. See
111     // ResourceManagerMediator.
112     // We encapsulate fallback logic in a fallback iterator class, so that 
113     // this logic isn't duplicated in several methods.
114     // 
115     // In the future, we can look into either breaking serialization if we
116     // decide this doesn't make sense for ResourceManager (i.e. how common
117     // is the scenario), manually make serialization work by providing 
118     // appropriate OnSerialization, Deserialization methods. We can also 
119     // look into further factoring and better design of IResourceGroveler
120     // interface to accommodate unused parameters that don't make sense
121     // for either file-based or manifest-based lookup paths.
122     //
123     // Benefits of this refactoring:
124     // - Makes it possible to understand what the ResourceManager does, 
125     // which is key for maintainability. 
126     // - Makes the ResourceManager more extensible by identifying and
127     // encapsulating what varies
128     // - Unearthed a bug that's been lurking a while in file-based 
129     // lookup paths for InternalGetResourceSet if createIfNotExists is
130     // false.
131     // - Reuses logic, e.g. by breaking apart the culture fallback into 
132     // the fallback iterator class, we don't have to repeat the 
133     // sometimes confusing fallback logic across multiple methods
134     // - Fxcop violations reduced to 1/5th of original count. Most 
135     // importantly, code complexity violations disappeared.
136     // - Finally, it got rid of dead code paths. Because the big loop was
137     // so confusing, it masked unused chunks of code. Also, dividing 
138     // between file-based and manifest-based allowed functionaliy 
139     // unused in silverlight to fall out.
140     // 
141     // Note: this type is integral to the construction of exception objects,
142     // and sometimes this has to be done in low memory situtations (OOM) or
143     // to create TypeInitializationExceptions due to failure of a static class
144     // constructor. This type needs to be extremely careful and assume that 
145     // any type it references may have previously failed to construct, so statics
146     // belonging to that type may not be initialized. FrameworkEventSource.Log
147     // is one such example.
148     //
149
150     public class ResourceManager
151     {
152         internal class CultureNameResourceSetPair
153         {
154             public String lastCultureName;
155             public ResourceSet lastResourceSet;
156         }
157
158         protected String BaseNameField;
159         // Sets is a many-to-one table of CultureInfos mapped to ResourceSets.
160         // Don't synchronize ResourceSets - too fine-grained a lock to be effective
161         [Obsolete("call InternalGetResourceSet instead")]
162         internal Hashtable ResourceSets;
163
164
165         // don't serialize the cache of ResourceSets
166         [NonSerialized]
167         private Dictionary<String, ResourceSet> _resourceSets;
168         private String moduleDir;      // For assembly-ignorant directory location
169         protected Assembly MainAssembly;   // Need the assembly manifest sometimes.
170         private Type _locationInfo;    // For Assembly or type-based directory layout
171         private Type _userResourceSet;  // Which ResourceSet instance to create
172         private CultureInfo _neutralResourcesCulture;  // For perf optimizations.
173
174         [NonSerialized]
175         private CultureNameResourceSetPair _lastUsedResourceCache;
176
177         private bool _ignoreCase;   // Whether case matters in GetString & GetObject
178
179         private bool UseManifest;  // Use Assembly manifest, or grovel disk.
180
181         // unused! But need to keep for serialization
182         [OptionalField(VersionAdded = 1)]
183         private bool UseSatelliteAssem;  // Are all the .resources files in the 
184                                          // main assembly, or in satellite assemblies for each culture?
185 #if RESOURCE_SATELLITE_CONFIG
186         private static volatile Hashtable _installedSatelliteInfo;  // Give the user the option  
187                                                                     // to prevent certain satellite assembly probes via a config file.
188                                                                     // Note that config files are per-appdomain, not per-assembly nor process
189         private static volatile bool _checkedConfigFile;  // Did we read the app's config file?
190 #endif
191
192         // Whether to fall back to the main assembly or a particular 
193         // satellite for the neutral resources.
194         [OptionalField]
195         private UltimateResourceFallbackLocation _fallbackLoc;
196         // Version number of satellite assemblies to look for.  May be null.
197         [OptionalField]
198         private Version _satelliteContractVersion;
199         [OptionalField]
200         private bool _lookedForSatelliteContractVersion;
201
202         // unused! But need to keep for serialization
203         [OptionalField(VersionAdded = 1)]
204         private Assembly _callingAssembly;  // Assembly who created the ResMgr.
205
206         // replaces _callingAssembly
207         [OptionalField(VersionAdded = 4)]
208         private RuntimeAssembly m_callingAssembly;  // Assembly who created the ResMgr.
209
210         // no need to serialize this; just create a new one on deserialization
211         [NonSerialized]
212         private IResourceGroveler resourceGroveler;
213
214         public static readonly int MagicNumber = unchecked((int)0xBEEFCACE);  // If only hex had a K...
215
216         // Version number so ResMgr can get the ideal set of classes for you.
217         // ResMgr header is:
218         // 1) MagicNumber (little endian Int32)
219         // 2) HeaderVersionNumber (little endian Int32)
220         // 3) Num Bytes to skip past ResMgr header (little endian Int32)
221         // 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
222         // 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
223         public static readonly int HeaderVersionNumber = 1;
224
225         //
226         //It would be better if we could use _neutralCulture instead of calling
227         //CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor.  CultureInfo 
228         //initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
229         //there yet because CultureInfo's class initializer hasn't finished.  If we move SystemResMgr off of
230         //Assembly (or at least make it an internal property) we should be able to circumvent this problem.
231         //
232         //      private static CultureInfo _neutralCulture = null;
233
234         // This is our min required ResourceSet type.
235         private static readonly Type _minResourceSet = typeof(ResourceSet);
236         // These Strings are used to avoid using Reflection in CreateResourceSet.
237         // The first set are used by ResourceWriter.  The second are used by
238         // InternalResGen.
239         internal static readonly String ResReaderTypeName = typeof(ResourceReader).FullName;
240         internal static readonly String ResSetTypeName = typeof(RuntimeResourceSet).FullName;
241         internal static readonly String MscorlibName = typeof(ResourceReader).Assembly.FullName;
242         internal const String ResFileExtension = ".resources";
243         internal const int ResFileExtensionLength = 10;
244
245         private static volatile bool s_IsAppXModel;
246
247         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
248         private void Init()
249         {
250             m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
251         }
252
253         protected ResourceManager()
254         {
255             Init();
256
257             _lastUsedResourceCache = new CultureNameResourceSetPair();
258             ResourceManagerMediator mediator = new ResourceManagerMediator(this);
259             resourceGroveler = new ManifestBasedResourceGroveler(mediator);
260         }
261
262         // Constructs a Resource Manager for files beginning with 
263         // baseName in the directory specified by resourceDir
264         // or in the current directory.  This Assembly-ignorant constructor is 
265         // mostly useful for testing your own ResourceSet implementation.
266         //
267         // A good example of a baseName might be "Strings".  BaseName 
268         // should not end in ".resources".
269         //
270         // Note: System.Windows.Forms uses this method at design time.
271         // 
272         private ResourceManager(String baseName, String resourceDir, Type usingResourceSet)
273         {
274             if (null == baseName)
275                 throw new ArgumentNullException(nameof(baseName));
276             if (null == resourceDir)
277                 throw new ArgumentNullException(nameof(resourceDir));
278
279             BaseNameField = baseName;
280
281             moduleDir = resourceDir;
282             _userResourceSet = usingResourceSet;
283 #pragma warning disable 618
284             ResourceSets = new Hashtable(); // for backward compatibility
285 #pragma warning restore 618
286             _resourceSets = new Dictionary<String, ResourceSet>();
287             _lastUsedResourceCache = new CultureNameResourceSetPair();
288             UseManifest = false;
289
290             ResourceManagerMediator mediator = new ResourceManagerMediator(this);
291             resourceGroveler = new FileBasedResourceGroveler(mediator);
292         }
293
294         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
295         public ResourceManager(String baseName, Assembly assembly)
296         {
297             if (null == baseName)
298                 throw new ArgumentNullException(nameof(baseName));
299
300             if (null == assembly)
301                 throw new ArgumentNullException(nameof(assembly));
302
303             if (!(assembly is RuntimeAssembly))
304                 throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
305
306             MainAssembly = assembly;
307             BaseNameField = baseName;
308
309             SetAppXConfiguration();
310
311             CommonAssemblyInit();
312
313             m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
314             // Special case for mscorlib - protect mscorlib's private resources.
315             // This isn't for security reasons, but to ensure we can make
316             // breaking changes to mscorlib's internal resources without 
317             // assuming users may have taken a dependency on them.
318             if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
319             {
320                 m_callingAssembly = null;
321             }
322         }
323
324         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
325         public ResourceManager(String baseName, Assembly assembly, Type usingResourceSet)
326         {
327             if (null == baseName)
328                 throw new ArgumentNullException(nameof(baseName));
329             if (null == assembly)
330                 throw new ArgumentNullException(nameof(assembly));
331
332             if (!(assembly is RuntimeAssembly))
333                 throw new ArgumentException(SR.Argument_MustBeRuntimeAssembly);
334
335             MainAssembly = assembly;
336             BaseNameField = baseName;
337
338             if (usingResourceSet != null && (usingResourceSet != _minResourceSet) && !(usingResourceSet.IsSubclassOf(_minResourceSet)))
339                 throw new ArgumentException(SR.Arg_ResMgrNotResSet, nameof(usingResourceSet));
340             _userResourceSet = usingResourceSet;
341
342             CommonAssemblyInit();
343             m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
344             // Special case for mscorlib - protect mscorlib's private resources.
345             // This isn't for security reasons, but to ensure we can make
346             // breaking changes to mscorlib's internal resources without 
347             // assuming users may have taken a dependency on them.
348             if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
349                 m_callingAssembly = null;
350         }
351
352         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
353         public ResourceManager(Type resourceSource)
354         {
355             if (null == resourceSource)
356                 throw new ArgumentNullException(nameof(resourceSource));
357
358             if (!(resourceSource is RuntimeType))
359                 throw new ArgumentException(SR.Argument_MustBeRuntimeType);
360
361             _locationInfo = resourceSource;
362             MainAssembly = _locationInfo.Assembly;
363             BaseNameField = resourceSource.Name;
364
365             SetAppXConfiguration();
366
367             CommonAssemblyInit();
368
369             m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
370             // Special case for mscorlib - protect mscorlib's private resources.
371             if (MainAssembly == typeof(Object).Assembly && m_callingAssembly != MainAssembly)
372             {
373                 m_callingAssembly = null;
374             }
375         }
376
377         [OnDeserializing]
378         private void OnDeserializing(StreamingContext ctx)
379         {
380             _resourceSets = null;
381             resourceGroveler = null;
382             _lastUsedResourceCache = null;
383         }
384
385         [OnDeserialized]
386         private void OnDeserialized(StreamingContext ctx)
387         {
388             _resourceSets = new Dictionary<String, ResourceSet>();
389             _lastUsedResourceCache = new CultureNameResourceSetPair();
390             // set up resource groveler, depending on whether this ResourceManager
391             // is looking for files or assemblies
392             ResourceManagerMediator mediator = new ResourceManagerMediator(this);
393             if (UseManifest)
394             {
395                 resourceGroveler = new ManifestBasedResourceGroveler(mediator);
396             }
397             else
398             {
399                 resourceGroveler = new FileBasedResourceGroveler(mediator);
400             }
401
402             // correct callingAssembly for v2
403             if (m_callingAssembly == null)
404             {
405                 m_callingAssembly = (RuntimeAssembly)_callingAssembly;
406             }
407
408             // v2 does this lazily
409             if (UseManifest && _neutralResourcesCulture == null)
410             {
411                 _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
412             }
413         }
414
415         [OnSerializing]
416         private void OnSerializing(StreamingContext ctx)
417         {
418             // Initialize the fields Whidbey expects
419             _callingAssembly = m_callingAssembly;
420             UseSatelliteAssem = UseManifest;
421 #pragma warning disable 618
422             ResourceSets = new Hashtable(); // for backward compatibility
423 #pragma warning restore 618
424         }
425
426
427         // Trying to unify code as much as possible, even though having to do a
428         // security check in each constructor prevents it.
429         private void CommonAssemblyInit()
430         {
431             // Now we can use the managed resources even when using PRI's to support the APIs GetObject, GetStream...etc.
432             UseManifest = true;
433
434             _resourceSets = new Dictionary<String, ResourceSet>();
435             _lastUsedResourceCache = new CultureNameResourceSetPair();
436
437             _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
438
439             ResourceManagerMediator mediator = new ResourceManagerMediator(this);
440             resourceGroveler = new ManifestBasedResourceGroveler(mediator);
441
442             _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
443         }
444
445         // Gets the base name for the ResourceManager.
446         public virtual String BaseName
447         {
448             get { return BaseNameField; }
449         }
450
451         // Whether we should ignore the capitalization of resources when calling
452         // GetString or GetObject.
453         public virtual bool IgnoreCase
454         {
455             get { return _ignoreCase; }
456             set { _ignoreCase = value; }
457         }
458
459         // Returns the Type of the ResourceSet the ResourceManager uses
460         // to construct ResourceSets.
461         public virtual Type ResourceSetType
462         {
463             get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
464         }
465
466         protected UltimateResourceFallbackLocation FallbackLocation
467         {
468             get { return _fallbackLoc; }
469             set { _fallbackLoc = value; }
470         }
471
472         // Tells the ResourceManager to call Close on all ResourceSets and 
473         // release all resources.  This will shrink your working set by
474         // potentially a substantial amount in a running application.  Any
475         // future resource lookups on this ResourceManager will be as 
476         // expensive as the very first lookup, since it will need to search
477         // for files and load resources again.
478         // 
479         // This may be useful in some complex threading scenarios, where 
480         // creating a new ResourceManager isn't quite the correct behavior.
481         public virtual void ReleaseAllResources()
482         {
483             Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
484
485             // If any calls to Close throw, at least leave ourselves in a
486             // consistent state.
487             _resourceSets = new Dictionary<String, ResourceSet>();
488             _lastUsedResourceCache = new CultureNameResourceSetPair();
489
490             lock (localResourceSets)
491             {
492                 IDictionaryEnumerator setEnum = localResourceSets.GetEnumerator();
493
494                 while (setEnum.MoveNext())
495                 {
496                     ((ResourceSet)setEnum.Value).Close();
497                 }
498             }
499         }
500
501         public static ResourceManager CreateFileBasedResourceManager(String baseName, String resourceDir, Type usingResourceSet)
502         {
503             return new ResourceManager(baseName, resourceDir, usingResourceSet);
504         }
505
506         // Given a CultureInfo, GetResourceFileName generates the name for 
507         // the binary file for the given CultureInfo.  This method uses 
508         // CultureInfo's Name property as part of the file name for all cultures
509         // other than the invariant culture.  This method does not touch the disk, 
510         // and is used only to construct what a resource file name (suitable for
511         // passing to the ResourceReader constructor) or a manifest resource file
512         // name should look like.
513         // 
514         // This method can be overriden to look for a different extension,
515         // such as ".ResX", or a completely different format for naming files.
516         protected virtual String GetResourceFileName(CultureInfo culture)
517         {
518             // If this is the neutral culture, don't include the culture name.
519             if (culture.HasInvariantCultureName)
520             {
521                 return BaseNameField + ResFileExtension;
522             }
523             else
524             {
525                 CultureInfo.VerifyCultureName(culture.Name, throwException: true);
526                 return BaseNameField + "." + culture.Name + ResFileExtension;
527             }
528         }
529
530         // WARNING: This function must be kept in sync with ResourceFallbackManager.GetEnumerator()
531         // Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
532         internal ResourceSet GetFirstResourceSet(CultureInfo culture)
533         {
534             // Logic from ResourceFallbackManager.GetEnumerator()
535             if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name)
536             {
537                 culture = CultureInfo.InvariantCulture;
538             }
539
540             if (_lastUsedResourceCache != null)
541             {
542                 lock (_lastUsedResourceCache)
543                 {
544                     if (culture.Name == _lastUsedResourceCache.lastCultureName)
545                         return _lastUsedResourceCache.lastResourceSet;
546                 }
547             }
548
549             // Look in the ResourceSet table
550             Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
551             ResourceSet rs = null;
552             if (localResourceSets != null)
553             {
554                 lock (localResourceSets)
555                 {
556                     localResourceSets.TryGetValue(culture.Name, out rs);
557                 }
558             }
559
560             if (rs != null)
561             {
562                 // update the cache with the most recent ResourceSet
563                 if (_lastUsedResourceCache != null)
564                 {
565                     lock (_lastUsedResourceCache)
566                     {
567                         _lastUsedResourceCache.lastCultureName = culture.Name;
568                         _lastUsedResourceCache.lastResourceSet = rs;
569                     }
570                 }
571                 return rs;
572             }
573
574             return null;
575         }
576
577         // Looks up a set of resources for a particular CultureInfo.  This is
578         // not useful for most users of the ResourceManager - call 
579         // GetString() or GetObject() instead.  
580         //
581         // The parameters let you control whether the ResourceSet is created 
582         // if it hasn't yet been loaded and if parent CultureInfos should be 
583         // loaded as well for resource inheritance.
584         //         
585         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
586         public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
587         {
588             if (null == culture)
589                 throw new ArgumentNullException(nameof(culture));
590
591             Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
592             ResourceSet rs;
593             if (localResourceSets != null)
594             {
595                 lock (localResourceSets)
596                 {
597                     if (localResourceSets.TryGetValue(culture.Name, out rs))
598                         return rs;
599                 }
600             }
601
602             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
603
604             if (UseManifest && culture.HasInvariantCultureName)
605             {
606                 string fileName = GetResourceFileName(culture);
607                 RuntimeAssembly mainAssembly = (RuntimeAssembly)MainAssembly;
608                 Stream stream = mainAssembly.GetManifestResourceStream(_locationInfo, fileName, m_callingAssembly == MainAssembly, ref stackMark);
609                 if (createIfNotExists && stream != null)
610                 {
611                     rs = ((ManifestBasedResourceGroveler)resourceGroveler).CreateResourceSet(stream, MainAssembly);
612                     AddResourceSet(localResourceSets, culture.Name, ref rs);
613                     return rs;
614                 }
615             }
616
617             // Note: ideally we could plumb through the stack crawl mark here, but we must
618             // call the virtual 3-argument InternalGetResourceSet method for compatibility.
619             // Security-wise, we're not overly interested in protecting access to resources,
620             // since full-trust callers can get them already and most resources are public.
621             // Also, the JIT inliner could always inline a caller into another assembly's
622             // method.
623             // So if we happen to return some resources in cases where we should really be
624             // doing a demand for member access permissions, we're not overly concerned.
625             return InternalGetResourceSet(culture, createIfNotExists, tryParents);
626         }
627
628         // InternalGetResourceSet is a non-threadsafe method where all the logic
629         // for getting a resource set lives.  Access to it is controlled by
630         // threadsafe methods such as GetResourceSet, GetString, & GetObject.  
631         // This will take a minimal number of locks.
632         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
633         protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
634         {
635             Debug.Assert(culture != null, "culture != null");
636
637             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
638             return InternalGetResourceSet(culture, createIfNotExists, tryParents, ref stackMark);
639         }
640
641         // InternalGetResourceSet is a non-threadsafe method where all the logic
642         // for getting a resource set lives.  Access to it is controlled by
643         // threadsafe methods such as GetResourceSet, GetString, & GetObject.  
644         // This will take a minimal number of locks.
645         private ResourceSet InternalGetResourceSet(CultureInfo requestedCulture, bool createIfNotExists, bool tryParents, ref StackCrawlMark stackMark)
646         {
647             Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
648             ResourceSet rs = null;
649             CultureInfo foundCulture = null;
650             lock (localResourceSets)
651             {
652                 if (localResourceSets.TryGetValue(requestedCulture.Name, out rs))
653                 {
654                     return rs;
655                 }
656             }
657
658             ResourceFallbackManager mgr = new ResourceFallbackManager(requestedCulture, _neutralResourcesCulture, tryParents);
659
660             foreach (CultureInfo currentCultureInfo in mgr)
661             {
662                 lock (localResourceSets)
663                 {
664                     if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs))
665                     {
666                         // we need to update the cache if we fellback
667                         if (requestedCulture != currentCultureInfo) foundCulture = currentCultureInfo;
668                         break;
669                     }
670                 }
671
672                 // InternalGetResourceSet will never be threadsafe.  However, it must
673                 // be protected against reentrancy from the SAME THREAD.  (ie, calling
674                 // GetSatelliteAssembly may send some window messages or trigger the
675                 // Assembly load event, which could fail then call back into the 
676                 // ResourceManager).  It's happened.
677
678                 rs = resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets,
679                                                            tryParents, createIfNotExists, ref stackMark);
680
681                 // found a ResourceSet; we're done
682                 if (rs != null)
683                 {
684                     foundCulture = currentCultureInfo;
685                     break;
686                 }
687             }
688
689             if (rs != null && foundCulture != null)
690             {
691                 // add entries to the cache for the cultures we have gone through
692
693                 // currentCultureInfo now refers to the culture that had resources.
694                 // update cultures starting from requested culture up to the culture
695                 // that had resources.
696                 foreach (CultureInfo updateCultureInfo in mgr)
697                 {
698                     AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);
699
700                     // stop when we've added current or reached invariant (top of chain)
701                     if (updateCultureInfo == foundCulture)
702                     {
703                         break;
704                     }
705                 }
706             }
707
708             return rs;
709         }
710
711         // Simple helper to ease maintenance and improve readability.
712         private static void AddResourceSet(Dictionary<String, ResourceSet> localResourceSets, String cultureName, ref ResourceSet rs)
713         {
714             // InternalGetResourceSet is both recursive and reentrant - 
715             // assembly load callbacks in particular are a way we can call
716             // back into the ResourceManager in unexpectedly on the same thread.
717             lock (localResourceSets)
718             {
719                 // If another thread added this culture, return that.
720                 ResourceSet lostRace;
721                 if (localResourceSets.TryGetValue(cultureName, out lostRace))
722                 {
723                     if (!Object.ReferenceEquals(lostRace, rs))
724                     {
725                         // Note: In certain cases, we can be trying to add a ResourceSet for multiple
726                         // cultures on one thread, while a second thread added another ResourceSet for one
727                         // of those cultures.  If there is a race condition we must make sure our ResourceSet 
728                         // isn't in our dictionary before closing it.
729                         if (!localResourceSets.ContainsValue(rs))
730                             rs.Dispose();
731                         rs = lostRace;
732                     }
733                 }
734                 else
735                 {
736                     localResourceSets.Add(cultureName, rs);
737                 }
738             }
739         }
740
741         protected static Version GetSatelliteContractVersion(Assembly a)
742         {
743             // Ensure that the assembly reference is not null
744             if (a == null)
745             {
746                 throw new ArgumentNullException(nameof(a), SR.ArgumentNull_Assembly);
747             }
748
749             // Return null. The calling code will use the assembly version instead to avoid potential type
750             // and library loads caused by CA lookup. NetCF uses the assembly version always.
751             return null;
752         }
753
754         protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
755         {
756             // This method should be obsolete - replace it with the one below.
757             // Unfortunately, we made it protected.
758             UltimateResourceFallbackLocation ignoringUsefulData = UltimateResourceFallbackLocation.MainAssembly;
759             CultureInfo culture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, ref ignoringUsefulData);
760             return culture;
761         }
762
763         // IGNORES VERSION
764         internal static bool CompareNames(String asmTypeName1,
765                                           String typeName2,
766                                           AssemblyName asmName2)
767         {
768             Debug.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null");
769
770             // First, compare type names
771             int comma = asmTypeName1.IndexOf(',');
772             if (((comma == -1) ? asmTypeName1.Length : comma) != typeName2.Length)
773                 return false;
774
775             // case sensitive
776             if (String.Compare(asmTypeName1, 0, typeName2, 0, typeName2.Length, StringComparison.Ordinal) != 0)
777                 return false;
778             if (comma == -1)
779                 return true;
780
781             // Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
782             // also, for  mscorlib ignores everything, since that's what the binder is going to do
783             while (Char.IsWhiteSpace(asmTypeName1[++comma])) ;
784
785             // case insensitive
786             AssemblyName an1 = new AssemblyName(asmTypeName1.Substring(comma));
787             if (String.Compare(an1.Name, asmName2.Name, StringComparison.OrdinalIgnoreCase) != 0)
788                 return false;
789
790             // to match IsMscorlib() in VM
791             if (String.Compare(an1.Name, System.CoreLib.Name, StringComparison.OrdinalIgnoreCase) == 0)
792                 return true;
793
794
795             if ((an1.CultureInfo != null) && (asmName2.CultureInfo != null) &&
796 #if FEATURE_USE_LCID                
797                 (an1.CultureInfo.LCID != asmName2.CultureInfo.LCID)
798 #else
799                 (an1.CultureInfo.Name != asmName2.CultureInfo.Name)
800 #endif                
801                 )
802                 return false;
803
804             byte[] pkt1 = an1.GetPublicKeyToken();
805             byte[] pkt2 = asmName2.GetPublicKeyToken();
806             if ((pkt1 != null) && (pkt2 != null))
807             {
808                 if (pkt1.Length != pkt2.Length)
809                     return false;
810
811                 for (int i = 0; i < pkt1.Length; i++)
812                 {
813                     if (pkt1[i] != pkt2[i])
814                         return false;
815                 }
816             }
817
818             return true;
819         }
820
821 #if FEATURE_APPX
822         private string GetStringFromPRI(String stringName, String startingCulture, String neutralResourcesCulture)
823         {
824             Debug.Assert(_bUsingModernResourceManagement);
825             Debug.Assert(_WinRTResourceManager != null);
826             Debug.Assert(_PRIonAppXInitialized);
827             Debug.Assert(AppDomain.IsAppXModel());
828
829             if (stringName.Length == 0)
830                 return null;
831
832             string resourceString = null;
833
834             // Do not handle exceptions. See the comment in SetAppXConfiguration about throwing
835             // exception types that the ResourceManager class is not documented to throw.
836             resourceString = _WinRTResourceManager.GetString(
837                                        stringName,
838                                        String.IsNullOrEmpty(startingCulture) ? null : startingCulture,
839                                        String.IsNullOrEmpty(neutralResourcesCulture) ? null : neutralResourcesCulture);
840
841             return resourceString;
842         }
843
844         // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the type via reflection.
845         // It would be better if we could just implement WindowsRuntimeResourceManager in mscorlib, but we can't, because
846         // we can do very little with WinRT in mscorlib.
847         internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
848         {
849             Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
850             return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
851         }
852 #endif
853
854         [NonSerialized]
855         private bool _bUsingModernResourceManagement; // Written only by SetAppXConfiguration
856
857 #if FEATURE_APPX
858         [NonSerialized]
859         private WindowsRuntimeResourceManagerBase _WinRTResourceManager; // Written only by SetAppXConfiguration
860
861         [NonSerialized]
862         private bool _PRIonAppXInitialized; // Written only by SetAppXConfiguration
863
864         [NonSerialized]
865         private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration
866
867         // When running under AppX, the following rules apply for resource lookup:
868         //
869         // 1) For Framework assemblies, we always use satellite assembly based lookup.
870         // 2) For non-FX assemblies:
871         //    
872         //    a) If the assembly lives under PLATFORM_RESOURCE_ROOTS (as specified by the host during AppDomain creation),
873         //       then we will use satellite assembly based lookup in assemblies like *.resources.dll.
874         //   
875         //    b) For any other non-FX assembly, we will use the modern resource manager with the premise that app package
876         //       contains the PRI resources.
877         private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(RuntimeAssembly resourcesAssembly)
878         {
879             bool fUseSatelliteAssemblyResourceLookupUnderAppX = typeof(Object).Assembly == resourcesAssembly;
880
881             if (!fUseSatelliteAssemblyResourceLookupUnderAppX)
882             {
883                 // Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
884                 String platformResourceRoots = (String)(AppDomain.CurrentDomain.GetData("PLATFORM_RESOURCE_ROOTS"));
885                 if ((platformResourceRoots != null) && (platformResourceRoots != String.Empty))
886                 {
887                     string resourceAssemblyPath = resourcesAssembly.Location;
888
889                     // Loop through the PLATFORM_RESOURCE_ROOTS and see if the assembly is contained in it.
890                     foreach (string pathPlatformResourceRoot in platformResourceRoots.Split(Path.PathSeparator))
891                     {
892                         if (resourceAssemblyPath.StartsWith(pathPlatformResourceRoot, StringComparison.CurrentCultureIgnoreCase))
893                         {
894                             // Found the resource assembly to be present in one of the PLATFORM_RESOURCE_ROOT, so stop the enumeration loop.
895                             fUseSatelliteAssemblyResourceLookupUnderAppX = true;
896                             break;
897                         }
898                     }
899                 }
900             }
901
902             return fUseSatelliteAssemblyResourceLookupUnderAppX;
903         }
904 #endif // FEATURE_APPX
905
906         // Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
907         // Throws MissingManifestResourceException and WinRT HResults
908
909         private void SetAppXConfiguration()
910         {
911             Debug.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member
912 #if FEATURE_APPX
913             Debug.Assert(_WinRTResourceManager == null); // Only this function writes to this member
914             Debug.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
915             Debug.Assert(_PRIExceptionInfo == null); // Only this function writes to this member
916
917             bool bUsingSatelliteAssembliesUnderAppX = false;
918
919             RuntimeAssembly resourcesAssembly = (RuntimeAssembly)MainAssembly;
920
921             if (resourcesAssembly == null)
922                 resourcesAssembly = m_callingAssembly;
923
924             if (resourcesAssembly != null)
925             {
926                 if (resourcesAssembly != typeof(Object).Assembly) // We are not loading resources for mscorlib
927                 {
928                     // Cannot load the WindowsRuntimeResourceManager when in a compilation process, since it
929                     // lives in System.Runtime.WindowsRuntime and only mscorlib may be loaded for execution.
930                     if (AppDomain.IsAppXModel())
931                     {
932                         s_IsAppXModel = true;
933
934                         // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField.
935                         String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName;
936
937                         // The only way this can happen is if a class inherited from ResourceManager and
938                         // did not set the BaseNameField before calling the protected ResourceManager() constructor.
939                         // For other constructors, we would already have thrown an ArgumentNullException by now.
940                         // Throwing an ArgumentNullException now is not the right thing to do because technically
941                         // ResourceManager() takes no arguments, and because it is not documented as throwing
942                         // any exceptions. Instead, let's go through the rest of the initialization with this set to
943                         // an empty string. We may in fact fail earlier for another reason, but otherwise we will
944                         // throw a MissingManifestResourceException when GetString is called indicating that a
945                         // resW filename called "" could not be found.
946                         if (reswFilename == null)
947                             reswFilename = String.Empty;
948
949                         WindowsRuntimeResourceManagerBase WRRM = null;
950                         bool bWRRM_Initialized = false;
951
952                         if (AppDomain.IsAppXDesignMode())
953                         {
954                             WRRM = GetWinRTResourceManager();
955                             try
956                             {
957                                 PRIExceptionInfo exceptionInfo; // If the exception info is filled in, we will ignore it.
958                                 bWRRM_Initialized = WRRM.Initialize(resourcesAssembly.Location, reswFilename, out exceptionInfo);
959                                 bUsingSatelliteAssembliesUnderAppX = !bWRRM_Initialized;
960                             }
961                             catch (Exception e)
962                             {
963                                 bUsingSatelliteAssembliesUnderAppX = true;
964                                 if (e.IsTransient)
965                                     throw;
966                             }
967                         }
968
969                         if (!bUsingSatelliteAssembliesUnderAppX)
970                         {
971                             _bUsingModernResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(resourcesAssembly);
972
973                             if (_bUsingModernResourceManagement)
974                             {
975                                 // Only now are we certain that we need the PRI file.
976
977                                 // Note that if IsAppXDesignMode is false, we haven't checked if the PRI file exists.
978                                 // This is by design. We will find out in the call to WindowsRuntimeResourceManager.Initialize below.
979
980                                 // At this point it is important NOT to set _bUsingModernResourceManagement to false
981                                 // if the PRI file does not exist because we are now certain we need to load PRI
982                                 // resources. We want to fail by throwing a MissingManifestResourceException
983                                 // if WindowsRuntimeResourceManager.Initialize fails to locate the PRI file. We do not
984                                 // want to fall back to using satellite assemblies anymore. Note that we would not throw
985                                 // the MissingManifestResourceException from this function, but from GetString. See the
986                                 // comment below on the reason for this.
987
988                                 if (WRRM != null && bWRRM_Initialized)
989                                 {
990                                     // Reuse the one successfully created earlier
991                                     _WinRTResourceManager = WRRM;
992                                     _PRIonAppXInitialized = true;
993                                 }
994                                 else
995                                 {
996                                     _WinRTResourceManager = GetWinRTResourceManager();
997
998                                     try
999                                     {
1000                                         _PRIonAppXInitialized = _WinRTResourceManager.Initialize(resourcesAssembly.Location, reswFilename, out _PRIExceptionInfo);
1001
1002                                         // Note that _PRIExceptionInfo might be null - this is OK.
1003                                         // In that case we will just throw the generic
1004                                         // MissingManifestResource_NoPRIresources exception.
1005                                         // See the implementation of GetString for more details.
1006                                     }
1007                                     // We would like to be able to throw a MissingManifestResourceException here if PRI resources
1008                                     // could not be loaded for a recognized reason. However, the ResourceManager constructors
1009                                     // that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException,
1010                                     // and since they are part of the portable profile, we cannot start throwing a new exception type
1011                                     // as that would break existing portable libraries. Hence we must save the exception information
1012                                     // now and throw the exception on the first call to GetString.
1013                                     catch (FileNotFoundException)
1014                                     {
1015                                         // We will throw MissingManifestResource_NoPRIresources from GetString
1016                                         // when we see that _PRIonAppXInitialized is false.
1017                                     }
1018                                     catch (Exception e)
1019                                     {
1020                                         // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps
1021                                         // in WindowsRuntimeResourceManager.Initialize.
1022                                         // In this case _PRIExceptionInfo is now null and we will just throw the generic
1023                                         // MissingManifestResource_NoPRIresources exception.
1024                                         // See the implementation of GetString for more details.
1025                                         if (e.HResult != HResults.ERROR_MRM_MAP_NOT_FOUND)
1026                                             throw; // Unexpected exception code. Bubble it up to the caller.
1027                                     }
1028
1029                                     if (!_PRIonAppXInitialized)
1030                                     {
1031                                         _bUsingModernResourceManagement = false;
1032                                     }
1033                                     // Allow all other exception types to bubble up to the caller.
1034
1035                                     // Yes, this causes us to potentially throw exception types that are not documented.
1036
1037                                     // Ultimately the tradeoff is the following:
1038                                     // -We could ignore unknown exceptions or rethrow them as inner exceptions
1039                                     // of exceptions that the ResourceManager class is already documented as throwing.
1040                                     // This would allow existing portable libraries to gracefully recover if they don't care
1041                                     // too much about the ResourceManager object they are using. However it could
1042                                     // mask potentially fatal errors that we are not aware of, such as a disk drive failing.
1043
1044
1045                                     // The alternative, which we chose, is to throw unknown exceptions. This may tear
1046                                     // down the process if the portable library and app don't expect this exception type.
1047                                     // On the other hand, this won't mask potentially fatal errors we don't know about.
1048                                 }
1049                             }
1050                         }
1051                     }
1052                 }
1053             }
1054             // resourcesAssembly == null should not happen but it can. See the comment on Assembly.GetCallingAssembly.
1055             // However for the sake of 100% backwards compatibility on Win7 and below, we must leave
1056             // _bUsingModernResourceManagement as false.
1057 #endif // FEATURE_APPX            
1058         }
1059
1060         // Looks up a resource value for a particular name.  Looks in the 
1061         // current thread's CultureInfo, and if not found, all parent CultureInfos.
1062         // Returns null if the resource wasn't found.
1063         // 
1064         public virtual String GetString(String name)
1065         {
1066             return GetString(name, (CultureInfo)null);
1067         }
1068
1069         // Looks up a resource value for a particular name.  Looks in the 
1070         // specified CultureInfo, and if not found, all parent CultureInfos.
1071         // Returns null if the resource wasn't found.
1072         // 
1073         public virtual String GetString(String name, CultureInfo culture)
1074         {
1075             if (null == name)
1076                 throw new ArgumentNullException(nameof(name));
1077
1078 #if FEATURE_APPX
1079             if (s_IsAppXModel)
1080             {
1081                 // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
1082                 // null it out, so that we re-compute it.  If we use modern resource lookup, we may end up getting a "better"
1083                 // match, since CultureInfo objects can't represent all the different languages the AppX resource model supports.
1084                 // For classic resources, this causes us to ignore the languages list and instead use the older Win32 behavior,
1085                 // which is the design choice we've made. (See the call a little later to GetCurrentUICultureNoAppX()).
1086                 if (Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
1087                 {
1088                     culture = null;
1089                 }
1090             }
1091
1092             if (_bUsingModernResourceManagement)
1093             {
1094                 if (_PRIonAppXInitialized == false)
1095                 {
1096                     // Always throw if we did not fully succeed in initializing the WinRT Resource Manager.
1097
1098                     if (_PRIExceptionInfo != null && _PRIExceptionInfo._PackageSimpleName != null && _PRIExceptionInfo._ResWFile != null)
1099                         throw new MissingManifestResourceException(SR.Format(SR.MissingManifestResource_ResWFileNotLoaded, _PRIExceptionInfo._ResWFile, _PRIExceptionInfo._PackageSimpleName));
1100
1101                     throw new MissingManifestResourceException(SR.MissingManifestResource_NoPRIresources);
1102                 }
1103
1104                 // Throws WinRT hresults.
1105                 return GetStringFromPRI(name,
1106                                         culture == null ? null : culture.Name,
1107                                         _neutralResourcesCulture.Name);
1108             }
1109             else
1110 #endif // FEATURE_APPX
1111             {
1112                 if (culture == null)
1113                 {
1114                     // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
1115                     // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
1116                     culture = CultureInfo.CurrentUICulture;
1117                 }
1118
1119                 ResourceSet last = GetFirstResourceSet(culture);
1120
1121                 if (last != null)
1122                 {
1123                     String value = last.GetString(name, _ignoreCase);
1124                     if (value != null)
1125                         return value;
1126                 }
1127
1128
1129                 // This is the CultureInfo hierarchy traversal code for resource 
1130                 // lookups, similar but necessarily orthogonal to the ResourceSet 
1131                 // lookup logic.
1132                 ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
1133                 foreach (CultureInfo currentCultureInfo in mgr)
1134                 {
1135                     ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
1136                     if (rs == null)
1137                         break;
1138
1139                     if (rs != last)
1140                     {
1141                         String value = rs.GetString(name, _ignoreCase);
1142                         if (value != null)
1143                         {
1144                             // update last used ResourceSet
1145                             if (_lastUsedResourceCache != null)
1146                             {
1147                                 lock (_lastUsedResourceCache)
1148                                 {
1149                                     _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
1150                                     _lastUsedResourceCache.lastResourceSet = rs;
1151                                 }
1152                             }
1153                             return value;
1154                         }
1155
1156                         last = rs;
1157                     }
1158                 }
1159             }
1160
1161             return null;
1162         }
1163
1164
1165         // Looks up a resource value for a particular name.  Looks in the 
1166         // current thread's CultureInfo, and if not found, all parent CultureInfos.
1167         // Returns null if the resource wasn't found.
1168         // 
1169         public virtual Object GetObject(String name)
1170         {
1171             return GetObject(name, (CultureInfo)null, true);
1172         }
1173
1174         // Looks up a resource value for a particular name.  Looks in the 
1175         // specified CultureInfo, and if not found, all parent CultureInfos.
1176         // Returns null if the resource wasn't found.
1177         public virtual Object GetObject(String name, CultureInfo culture)
1178         {
1179             return GetObject(name, culture, true);
1180         }
1181
1182         private Object GetObject(String name, CultureInfo culture, bool wrapUnmanagedMemStream)
1183         {
1184             if (null == name)
1185                 throw new ArgumentNullException(nameof(name));
1186
1187 #if FEATURE_APPX
1188             if (s_IsAppXModel)
1189             {
1190                 // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
1191                 // null it out, so that we re-compute it based on the Win32 value and not the AppX language list value.
1192                 // (See the call a little later to GetCurrentUICultureNoAppX()).
1193                 if (Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
1194                 {
1195                     culture = null;
1196                 }
1197             }
1198 #endif
1199
1200             if (null == culture)
1201             {
1202                 // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
1203                 // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
1204                 culture = CultureInfo.GetCurrentUICultureNoAppX();
1205             }
1206
1207             ResourceSet last = GetFirstResourceSet(culture);
1208             if (last != null)
1209             {
1210                 Object value = last.GetObject(name, _ignoreCase);
1211
1212                 if (value != null)
1213                 {
1214                     UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
1215                     if (stream != null && wrapUnmanagedMemStream)
1216                         return new UnmanagedMemoryStreamWrapper(stream);
1217                     else
1218                         return value;
1219                 }
1220             }
1221
1222             // This is the CultureInfo hierarchy traversal code for resource 
1223             // lookups, similar but necessarily orthogonal to the ResourceSet 
1224             // lookup logic.
1225             ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
1226
1227             foreach (CultureInfo currentCultureInfo in mgr)
1228             {
1229                 // Note: Technically this method should be passed in a stack crawl mark that we then pass
1230                 // to InternalGetResourceSet for ensuring we demand permissions to read your private resources
1231                 // if you're reading resources from an assembly other than yourself.  But, we must call our
1232                 // three argument overload (without the stack crawl mark) for compatibility.  After 
1233                 // consideration, we aren't worried about the security impact.
1234                 ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
1235                 if (rs == null)
1236                     break;
1237
1238                 if (rs != last)
1239                 {
1240                     Object value = rs.GetObject(name, _ignoreCase);
1241                     if (value != null)
1242                     {
1243                         // update the last used ResourceSet
1244                         if (_lastUsedResourceCache != null)
1245                         {
1246                             lock (_lastUsedResourceCache)
1247                             {
1248                                 _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
1249                                 _lastUsedResourceCache.lastResourceSet = rs;
1250                             }
1251                         }
1252
1253                         UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
1254                         if (stream != null && wrapUnmanagedMemStream)
1255                             return new UnmanagedMemoryStreamWrapper(stream);
1256                         else
1257                             return value;
1258                     }
1259
1260                     last = rs;
1261                 }
1262             }
1263
1264             return null;
1265         }
1266
1267         public UnmanagedMemoryStream GetStream(String name)
1268         {
1269             return GetStream(name, (CultureInfo)null);
1270         }
1271
1272         public UnmanagedMemoryStream GetStream(String name, CultureInfo culture)
1273         {
1274             Object obj = GetObject(name, culture, false);
1275             UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
1276             if (ums == null && obj != null)
1277                 throw new InvalidOperationException(SR.Format(SR.InvalidOperation_ResourceNotStream_Name, name));
1278             return ums;
1279         }
1280
1281 #if RESOURCE_SATELLITE_CONFIG
1282         // Internal helper method - gives an end user the ability to prevent
1283         // satellite assembly probes for certain cultures via a config file.
1284         private bool TryLookingForSatellite(CultureInfo lookForCulture)
1285         {
1286             if (!_checkedConfigFile)
1287             {
1288                 lock (this)
1289                 {
1290                     if (!_checkedConfigFile)
1291                     {
1292                         _checkedConfigFile = true;
1293                         _installedSatelliteInfo = GetSatelliteAssembliesFromConfig();
1294                     }
1295                 }
1296             }
1297
1298             if (_installedSatelliteInfo == null)
1299                 return true;
1300
1301             String[] installedSatellites = (String[])_installedSatelliteInfo[MainAssembly.FullName];
1302
1303             if (installedSatellites == null)
1304                 return true;
1305
1306             // The config file told us what satellites might be installed.
1307             int pos = Array.IndexOf(installedSatellites, lookForCulture.Name);
1308
1309             return pos >= 0;
1310         }
1311
1312         // Note: There is one config file per appdomain.  This is not 
1313         // per-process nor per-assembly.
1314         private Hashtable GetSatelliteAssembliesFromConfig()
1315         {
1316             return null;
1317         }
1318 #endif  // RESOURCE_SATELLITE_CONFIG
1319
1320         internal class ResourceManagerMediator
1321         {
1322             private ResourceManager _rm;
1323
1324             internal ResourceManagerMediator(ResourceManager rm)
1325             {
1326                 if (rm == null)
1327                 {
1328                     throw new ArgumentNullException(nameof(rm));
1329                 }
1330                 _rm = rm;
1331             }
1332
1333             // NEEDED ONLY BY FILE-BASED
1334             internal String ModuleDir
1335             {
1336                 get { return _rm.moduleDir; }
1337             }
1338
1339             // NEEDED BOTH BY FILE-BASED  AND ASSEMBLY-BASED
1340             internal Type LocationInfo
1341             {
1342                 get { return _rm._locationInfo; }
1343             }
1344
1345             internal Type UserResourceSet
1346             {
1347                 get { return _rm._userResourceSet; }
1348             }
1349
1350             internal String BaseNameField
1351             {
1352                 get { return _rm.BaseNameField; }
1353             }
1354
1355             internal CultureInfo NeutralResourcesCulture
1356             {
1357                 get { return _rm._neutralResourcesCulture; }
1358                 set { _rm._neutralResourcesCulture = value; }
1359             }
1360
1361             internal String GetResourceFileName(CultureInfo culture)
1362             {
1363                 return _rm.GetResourceFileName(culture);
1364             }
1365
1366             // NEEDED ONLY BY ASSEMBLY-BASED
1367             internal bool LookedForSatelliteContractVersion
1368             {
1369                 get { return _rm._lookedForSatelliteContractVersion; }
1370                 set { _rm._lookedForSatelliteContractVersion = value; }
1371             }
1372
1373             internal Version SatelliteContractVersion
1374             {
1375                 get { return _rm._satelliteContractVersion; }
1376                 set { _rm._satelliteContractVersion = value; }
1377             }
1378
1379             internal Version ObtainSatelliteContractVersion(Assembly a)
1380             {
1381                 return ResourceManager.GetSatelliteContractVersion(a);
1382             }
1383
1384             internal UltimateResourceFallbackLocation FallbackLoc
1385             {
1386                 get { return _rm.FallbackLocation; }
1387                 set { _rm._fallbackLoc = value; }
1388             }
1389
1390             internal RuntimeAssembly CallingAssembly
1391             {
1392                 get { return _rm.m_callingAssembly; }
1393             }
1394
1395             internal RuntimeAssembly MainAssembly
1396             {
1397                 get { return (RuntimeAssembly)_rm.MainAssembly; }
1398             }
1399
1400             // this is weird because we have BaseNameField accessor above, but we're sticking
1401             // with it for compat.
1402             internal String BaseName
1403             {
1404                 get { return _rm.BaseName; }
1405             }
1406
1407
1408 #if RESOURCE_SATELLITE_CONFIG
1409             internal bool TryLookingForSatellite(CultureInfo lookForCulture)
1410             {
1411                 return _rm.TryLookingForSatellite(lookForCulture);
1412             }
1413 #endif
1414
1415         }
1416     }
1417 }