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