Mono friendly System.Resources
[mono-project.git] / mcs / class / referencesource / mscorlib / system / resources / resourcemanager.cs
blob758f36b734633b8811940e15740456d3f6f8667e
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class: ResourceManager
9 **
10 ** <OWNER>jathaine</OWNER>
13 ** Purpose: Default way to access String and Object resources
14 ** from an assembly.
16 **
17 ===========================================================*/
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
40 #if FEATURE_APPX
42 // This is implemented in System.Runtime.WindowsRuntime as function System.Resources.WindowsRuntimeResourceManager,
43 // allowing us to ask for a WinRT-specific ResourceManager.
44 // Ideally this would be an interface, or at least an abstract class - but neither seems to play nice with FriendAccessAllowed.
46 [FriendAccessAllowed]
47 [SecurityCritical]
48 internal class WindowsRuntimeResourceManagerBase
50 [SecurityCritical]
51 public virtual bool Initialize(string libpath, string reswFilename, out PRIExceptionInfo exceptionInfo){exceptionInfo = null; return false;}
53 [SecurityCritical]
54 public virtual String GetString(String stringName, String startingCulture, String neutralResourcesCulture){return null;}
56 public virtual CultureInfo GlobalResourceContextBestFitCultureInfo {
57 [SecurityCritical]
58 get { return null; }
62 [FriendAccessAllowed]
63 // [[....] 3/9/2012] This class should be named PRIErrorInfo.
65 // During Dev11 CLR RC Ask mode, the Windows Modern Resource Manager
66 // made a breaking change such that ResourceMap.GetSubtree returns null when a subtree is
67 // not found instead of throwing an exception. As a result the name of this class is no longer accurate.
68 // It should be called PRIErrorInfo. However changing the name of this internal class would cause
69 // mscorlib.asmmeta and System.Runtime.WindowsRuntime.asmmeta to change,
70 // which would in turn require updating of the mscorlib and System.Runtime.WindowsRuntime
71 // reference assemblies under InternalApis. This would not meet the Ask Mode bar at this time.
72 // To get an idea of which files may need to be updated when updating this name,
73 // see changeset 399234 in the DevDiv2 database, though the update procedure may have changed
74 // by the time you read this.
75 internal class PRIExceptionInfo
77 public string _PackageSimpleName;
78 public string _ResWFile;
80 #endif // FEATURE_APPX
82 // Resource Manager exposes an assembly's resources to an application for
83 // the correct CultureInfo. An example would be localizing text for a
84 // user-visible message. Create a set of resource files listing a name
85 // for a message and its value, compile them using ResGen, put them in
86 // an appropriate place (your assembly manifest(?)), then create a Resource
87 // Manager and query for the name of the message you want. The Resource
88 // Manager will use CultureInfo.GetCurrentUICulture() to look
89 // up a resource for your user's locale settings.
90 //
91 // Users should ideally create a resource file for every culture, or
92 // at least a meaningful subset. The filenames will follow the naming
93 // scheme:
94 //
95 // basename.culture name.resources
96 //
97 // The base name can be the name of your application, or depending on
98 // the granularity desired, possibly the name of each class. The culture
99 // name is determined from CultureInfo's Name property.
100 // An example file name may be MyApp.en-US.resources for
101 // MyApp's US English resources.
103 // -----------------
104 // Refactoring Notes
105 // -----------------
106 // In Feb 08, began first step of refactoring ResourceManager to improve
107 // maintainability (sd changelist 3012100). This resulted in breaking
108 // apart the InternalGetResourceSet "big loop" so that the file-based
109 // and manifest-based lookup was located in separate methods.
110 // In Apr 08, continued refactoring so that file-based and manifest-based
111 // concerns are encapsulated by separate classes. At construction, the
112 // ResourceManager creates one of these classes based on whether the
113 // RM will need to use file-based or manifest-based resources, and
114 // afterwards refers to this through the interface IResourceGroveler.
116 // Serialization Compat: Ideally, we could have refactored further but
117 // this would have broken serialization compat. For example, the
118 // ResourceManager member UseManifest and UseSatelliteAssem are no
119 // longer relevant on ResourceManager. Similarly, other members could
120 // ideally be moved to the file-based or manifest-based classes
121 // because they are only relevant for those types of lookup.
123 // Solution now / in the future:
124 // For now, we simply use a mediator class so that we can keep these
125 // members on ResourceManager but allow the file-based and manifest-
126 // based classes to access/set these members in a uniform way. See
127 // ResourceManagerMediator.
128 // We encapsulate fallback logic in a fallback iterator class, so that
129 // this logic isn't duplicated in several methods.
131 // In the future, we can look into either breaking serialization if we
132 // decide this doesn't make sense for ResourceManager (i.e. how common
133 // is the scenario), manually make serialization work by providing
134 // appropriate OnSerialization, Deserialization methods. We can also
135 // look into further factoring and better design of IResourceGroveler
136 // interface to accommodate unused parameters that don't make sense
137 // for either file-based or manifest-based lookup paths.
139 // Benefits of this refactoring:
140 // - Makes it possible to understand what the ResourceManager does,
141 // which is key for maintainability.
142 // - Makes the ResourceManager more extensible by identifying and
143 // encapsulating what varies
144 // - Unearthed a bug that's been lurking a while in file-based
145 // lookup paths for InternalGetResourceSet if createIfNotExists is
146 // false.
147 // - Reuses logic, e.g. by breaking apart the culture fallback into
148 // the fallback iterator class, we don't have to repeat the
149 // sometimes confusing fallback logic across multiple methods
150 // - Fxcop violations reduced to 1/5th of original count. Most
151 // importantly, code complexity violations disappeared.
152 // - Finally, it got rid of dead code paths. Because the big loop was
153 // so confusing, it masked unused chunks of code. Also, dividing
154 // between file-based and manifest-based allowed functionaliy
155 // unused in silverlight to fall out.
157 // Note: this type is integral to the construction of exception objects,
158 // and sometimes this has to be done in low memory situtations (OOM) or
159 // to create TypeInitializationExceptions due to failure of a static class
160 // constructor. This type needs to be extremely careful and assume that
161 // any type it references may have previously failed to construct, so statics
162 // belonging to that type may not be initialized. FrameworkEventSource.Log
163 // is one such example.
166 [Serializable]
167 [System.Runtime.InteropServices.ComVisible(true)]
168 public class ResourceManager
171 internal class CultureNameResourceSetPair {
172 public String lastCultureName;
173 public ResourceSet lastResourceSet;
176 protected String BaseNameField;
177 // Sets is a many-to-one table of CultureInfos mapped to ResourceSets.
178 // Don't synchronize ResourceSets - too fine-grained a lock to be effective
179 [Obsolete("call InternalGetResourceSet instead")]
180 protected Hashtable ResourceSets;
183 // don't serialize the cache of ResourceSets
184 [NonSerialized]
185 private Dictionary <String,ResourceSet> _resourceSets;
186 private String moduleDir; // For assembly-ignorant directory location
187 protected Assembly MainAssembly; // Need the assembly manifest sometimes.
188 private Type _locationInfo; // For Assembly or type-based directory layout
189 private Type _userResourceSet; // Which ResourceSet instance to create
190 private CultureInfo _neutralResourcesCulture; // For perf optimizations.
192 [NonSerialized]
193 private CultureNameResourceSetPair _lastUsedResourceCache;
195 private bool _ignoreCase; // Whether case matters in GetString & GetObject
197 private bool UseManifest; // Use Assembly manifest, or grovel disk.
199 // unused! But need to keep for serialization
200 [OptionalField(VersionAdded = 1)]
201 private bool UseSatelliteAssem; // Are all the .resources files in the
202 // main assembly, or in satellite assemblies for each culture?
203 #if FEATURE_SPLIT_RESOURCES
204 private bool _isDebugSatellite;
205 #endif // FEATURE_SPLIT_RESOURCES
206 #if RESOURCE_SATELLITE_CONFIG
207 private static volatile Hashtable _installedSatelliteInfo; // Give the user the option
208 // to prevent certain satellite assembly probes via a config file.
209 // Note that config files are per-appdomain, not per-assembly nor process
210 private static volatile bool _checkedConfigFile; // Did we read the app's config file?
211 #endif
213 // Whether to fall back to the main assembly or a particular
214 // satellite for the neutral resources.
215 [OptionalField]
216 private UltimateResourceFallbackLocation _fallbackLoc;
217 // Version number of satellite assemblies to look for. May be null.
218 [OptionalField]
219 private Version _satelliteContractVersion;
220 [OptionalField]
221 private bool _lookedForSatelliteContractVersion;
223 // unused! But need to keep for serialization
224 [OptionalField(VersionAdded = 1)]
225 private Assembly _callingAssembly; // Assembly who created the ResMgr.
227 // replaces _callingAssembly
228 [OptionalField(VersionAdded = 4)]
229 private RuntimeAssembly m_callingAssembly; // Assembly who created the ResMgr.
231 // no need to serialize this; just create a new one on deserialization
232 [NonSerialized]
233 private IResourceGroveler resourceGroveler;
235 public static readonly int MagicNumber = unchecked((int)0xBEEFCACE); // If only hex had a K...
237 // Version number so ResMgr can get the ideal set of classes for you.
238 // ResMgr header is:
239 // 1) MagicNumber (little endian Int32)
240 // 2) HeaderVersionNumber (little endian Int32)
241 // 3) Num Bytes to skip past ResMgr header (little endian Int32)
242 // 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
243 // 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
244 public static readonly int HeaderVersionNumber = 1;
247 //It would be better if we could use _neutralCulture instead of calling
248 //CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor. CultureInfo
249 //initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
250 //there yet because CultureInfo's class initializer hasn't finished. If we move SystemResMgr off of
251 //Assembly (or at least make it an internal property) we should be able to circumvent this problem.
253 // private static CultureInfo _neutralCulture = null;
255 // This is our min required ResourceSet type.
256 private static readonly Type _minResourceSet = typeof(ResourceSet);
257 // These Strings are used to avoid using Reflection in CreateResourceSet.
258 // The first set are used by ResourceWriter. The second are used by
259 // InternalResGen.
260 internal static readonly String ResReaderTypeName = typeof(ResourceReader).FullName;
261 internal static readonly String ResSetTypeName = typeof(RuntimeResourceSet).FullName;
262 internal static readonly String MscorlibName = typeof(ResourceReader).Assembly.FullName;
263 internal const String ResFileExtension = ".resources";
264 internal const int ResFileExtensionLength = 10;
266 // My private debugging aid. Set to 5 or 6 for verbose output. Set to 3
267 // for summary level information.
268 internal static readonly int DEBUG = 0; //Making this const causes C# to consider all of the code that it guards unreachable.
270 private static volatile bool s_IsAppXModel;
272 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
273 private void Init()
275 m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
278 protected ResourceManager()
280 #if !FEATURE_CORECLR
281 // This constructor is not designed to be used under AppX and is not in the Win8 profile.
282 // However designers may use them even if they are running under AppX since they are
283 // not subject to the restrictions of the Win8 profile.
284 Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
285 #endif
287 Init();
289 _lastUsedResourceCache = new CultureNameResourceSetPair();
290 ResourceManagerMediator mediator = new ResourceManagerMediator(this);
291 resourceGroveler = new ManifestBasedResourceGroveler(mediator);
294 // Constructs a Resource Manager for files beginning with
295 // baseName in the directory specified by resourceDir
296 // or in the current directory. This Assembly-ignorant constructor is
297 // mostly useful for testing your own ResourceSet implementation.
299 // A good example of a baseName might be "Strings". BaseName
300 // should not end in ".resources".
302 // Note: System.Windows.Forms uses this method at design time.
304 [ResourceExposure(ResourceScope.Machine)]
305 [ResourceConsumption(ResourceScope.Machine)]
306 private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) {
307 if (null==baseName)
308 throw new ArgumentNullException("baseName");
309 if (null==resourceDir)
310 throw new ArgumentNullException("resourceDir");
311 Contract.EndContractBlock();
313 #if !FEATURE_CORECLR
314 // This constructor is not designed to be used under AppX and is not in the Win8 profile.
315 // However designers may use them even if they are running under AppX since they are
316 // not subject to the restrictions of the Win8 profile.
317 Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
318 #endif
320 BaseNameField = baseName;
322 moduleDir = resourceDir;
323 _userResourceSet = usingResourceSet;
324 #pragma warning disable 618
325 ResourceSets = new Hashtable(); // for backward compatibility
326 #pragma warning restore 618
327 _resourceSets = new Dictionary<String, ResourceSet>();
328 _lastUsedResourceCache = new CultureNameResourceSetPair();
329 UseManifest = false;
331 ResourceManagerMediator mediator = new ResourceManagerMediator(this);
332 resourceGroveler = new FileBasedResourceGroveler(mediator);
334 #if !FEATURE_CORECLR && !MONO // PAL doesn't support eventing, and we don't compile event providers for coreclr
335 if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
336 CultureInfo culture = CultureInfo.InvariantCulture;
337 String defaultResName = GetResourceFileName(culture);
339 if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
340 FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
342 else {
343 FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, defaultResName);
346 #endif
349 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
350 public ResourceManager(String baseName, Assembly assembly)
352 if (null==baseName)
353 throw new ArgumentNullException("baseName");
355 if (null==assembly)
356 throw new ArgumentNullException("assembly");
357 Contract.EndContractBlock();
359 if (!(assembly is RuntimeAssembly))
360 throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
362 MainAssembly = assembly;
363 BaseNameField = baseName;
365 SetAppXConfiguration();
367 CommonAssemblyInit();
369 m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
370 // Special case for mscorlib - protect mscorlib's private resources.
371 // This isn't for security reasons, but to ensure we can make
372 // breaking changes to mscorlib's internal resources without
373 // assuming users may have taken a dependency on them.
374 if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
376 m_callingAssembly = null;
380 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
381 public ResourceManager(String baseName, Assembly assembly, Type usingResourceSet)
383 if (null==baseName)
384 throw new ArgumentNullException("baseName");
385 if (null==assembly)
386 throw new ArgumentNullException("assembly");
387 Contract.EndContractBlock();
389 #if !FEATURE_CORECLR
390 // This constructor is not designed to be used under AppX and is not in the Win8 profile.
391 // However designers may use them even if they are running under AppX since they are
392 // not subject to the restrictions of the Win8 profile.
393 Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
394 #endif
396 if (!(assembly is RuntimeAssembly))
397 throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
399 MainAssembly = assembly;
400 BaseNameField = baseName;
402 if (usingResourceSet != null && (usingResourceSet != _minResourceSet) && !(usingResourceSet.IsSubclassOf(_minResourceSet)))
403 throw new ArgumentException(Environment.GetResourceString("Arg_ResMgrNotResSet"), "usingResourceSet");
404 _userResourceSet = usingResourceSet;
406 CommonAssemblyInit();
407 m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
408 // Special case for mscorlib - protect mscorlib's private resources.
409 // This isn't for security reasons, but to ensure we can make
410 // breaking changes to mscorlib's internal resources without
411 // assuming users may have taken a dependency on them.
412 if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
413 m_callingAssembly = null;
416 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
417 public ResourceManager(Type resourceSource)
419 if (null==resourceSource)
420 throw new ArgumentNullException("resourceSource");
421 Contract.EndContractBlock();
423 if (!(resourceSource is RuntimeType))
424 throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));
426 _locationInfo = resourceSource;
427 MainAssembly = _locationInfo.Assembly;
428 BaseNameField = resourceSource.Name;
430 SetAppXConfiguration();
432 CommonAssemblyInit();
434 m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
435 // Special case for mscorlib - protect mscorlib's private resources.
436 if (MainAssembly == typeof(Object).Assembly && m_callingAssembly != MainAssembly)
438 m_callingAssembly = null;
442 #if FEATURE_SPLIT_RESOURCES
443 internal ResourceManager(String baseName, Assembly assembly, bool isDebugSatellite) : this(baseName, assembly) {
444 _isDebugSatellite = isDebugSatellite;
446 #endif // FEATURE_SPLIT_RESOURCES
448 [OnDeserializing]
449 private void OnDeserializing(StreamingContext ctx)
451 this._resourceSets = null;
452 this.resourceGroveler = null;
453 this._lastUsedResourceCache = null;
456 [System.Security.SecuritySafeCritical]
457 [OnDeserialized]
458 private void OnDeserialized(StreamingContext ctx)
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)
467 resourceGroveler = new ManifestBasedResourceGroveler(mediator);
469 else
471 resourceGroveler = new FileBasedResourceGroveler(mediator);
474 // correct callingAssembly for v2
475 if (this.m_callingAssembly == null)
477 this.m_callingAssembly = (RuntimeAssembly)_callingAssembly;
480 // v2 does this lazily
481 if (UseManifest && this._neutralResourcesCulture == null)
483 _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
487 [OnSerializing]
488 private void OnSerializing(StreamingContext ctx)
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
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()
504 if (_bUsingModernResourceManagement == false)
506 UseManifest = true;
508 _resourceSets = new Dictionary<String,ResourceSet>();
509 _lastUsedResourceCache = new CultureNameResourceSetPair();
511 _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
513 ResourceManagerMediator mediator = new ResourceManagerMediator(this);
514 resourceGroveler = new ManifestBasedResourceGroveler(mediator);
517 _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
519 #if !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile event providers for coreclr
520 if (_bUsingModernResourceManagement == false)
522 #if !MONO
523 if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
524 CultureInfo culture = CultureInfo.InvariantCulture;
525 String defaultResName = GetResourceFileName(culture);
527 if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
528 FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
530 else {
531 String outputResName = defaultResName;
532 if (_locationInfo != null && _locationInfo.Namespace != null)
533 outputResName = _locationInfo.Namespace + Type.Delimiter + defaultResName;
534 FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, outputResName);
537 #endif
538 #pragma warning disable 618
539 ResourceSets = new Hashtable(); // for backward compatibility
540 #pragma warning restore 618
542 #endif
545 // Gets the base name for the ResourceManager.
546 public virtual String BaseName {
547 get { return BaseNameField; }
550 // Whether we should ignore the capitalization of resources when calling
551 // GetString or GetObject.
552 public virtual bool IgnoreCase {
553 get { return _ignoreCase; }
554 set { _ignoreCase = value; }
557 // Returns the Type of the ResourceSet the ResourceManager uses
558 // to construct ResourceSets.
559 public virtual Type ResourceSetType {
560 get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
563 protected UltimateResourceFallbackLocation FallbackLocation
565 get { return _fallbackLoc; }
566 set { _fallbackLoc = value; }
569 // Tells the ResourceManager to call Close on all ResourceSets and
570 // release all resources. This will shrink your working set by
571 // potentially a substantial amount in a running application. Any
572 // future resource lookups on this ResourceManager will be as
573 // expensive as the very first lookup, since it will need to search
574 // for files and load resources again.
576 // This may be useful in some complex threading scenarios, where
577 // creating a new ResourceManager isn't quite the correct behavior.
578 public virtual void ReleaseAllResources()
580 #if !FEATURE_CORECLR && !MONO
581 if (FrameworkEventSource.IsInitialized)
583 FrameworkEventSource.Log.ResourceManagerReleasingResources(BaseNameField, MainAssembly);
585 #endif
586 Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
588 // If any calls to Close throw, at least leave ourselves in a
589 // consistent state.
590 _resourceSets = new Dictionary<String,ResourceSet>();
591 _lastUsedResourceCache = new CultureNameResourceSetPair();
593 lock(localResourceSets) {
594 IDictionaryEnumerator setEnum = localResourceSets.GetEnumerator();
596 #if !FEATURE_CORECLR
597 IDictionaryEnumerator setEnum2 = null;
598 #pragma warning disable 618
599 if (ResourceSets != null) {
600 setEnum2 = ResourceSets.GetEnumerator();
602 ResourceSets = new Hashtable(); // for backwards compat
603 #pragma warning restore 618
604 #endif
606 while (setEnum.MoveNext()) {
607 ((ResourceSet)setEnum.Value).Close();
610 #if !FEATURE_CORECLR
611 if (setEnum2 != null) {
612 while (setEnum2.MoveNext()) {
613 ((ResourceSet)setEnum2.Value).Close();
616 #endif
620 [ResourceExposure(ResourceScope.Machine)]
621 [ResourceConsumption(ResourceScope.Machine)]
622 public static ResourceManager CreateFileBasedResourceManager(String baseName, String resourceDir, Type usingResourceSet)
624 return new ResourceManager(baseName, resourceDir, usingResourceSet);
627 // Given a CultureInfo, GetResourceFileName generates the name for
628 // the binary file for the given CultureInfo. This method uses
629 // CultureInfo's Name property as part of the file name for all cultures
630 // other than the invariant culture. This method does not touch the disk,
631 // and is used only to construct what a resource file name (suitable for
632 // passing to the ResourceReader constructor) or a manifest resource file
633 // name should look like.
635 // This method can be overriden to look for a different extension,
636 // such as ".ResX", or a completely different format for naming files.
637 protected virtual String GetResourceFileName(CultureInfo culture) {
638 StringBuilder sb = new StringBuilder(255);
639 sb.Append(BaseNameField);
640 // If this is the neutral culture, don't append culture name.
641 if (!culture.HasInvariantCultureName)
643 CultureInfo.VerifyCultureName(culture.Name, true);
644 sb.Append('.');
645 sb.Append(culture.Name);
647 sb.Append(ResFileExtension);
648 return sb.ToString();
651 // WARNING: This function must be kept in [....] with ResourceFallbackManager.GetEnumerator()
652 // Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
653 internal ResourceSet GetFirstResourceSet(CultureInfo culture)
655 // Logic from ResourceFallbackManager.GetEnumerator()
656 if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name)
658 culture = CultureInfo.InvariantCulture;
661 if(_lastUsedResourceCache != null) {
662 lock (_lastUsedResourceCache) {
663 if (culture.Name == _lastUsedResourceCache.lastCultureName)
664 return _lastUsedResourceCache.lastResourceSet;
668 // Look in the ResourceSet table
669 Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
670 ResourceSet rs = null;
671 if (localResourceSets != null)
673 lock (localResourceSets)
675 localResourceSets.TryGetValue(culture.Name, out rs);
679 if (rs != null)
681 // update the cache with the most recent ResourceSet
682 if (_lastUsedResourceCache != null) {
683 lock (_lastUsedResourceCache) {
684 _lastUsedResourceCache.lastCultureName = culture.Name;
685 _lastUsedResourceCache.lastResourceSet = rs;
688 return rs;
691 return null;
694 // Looks up a set of resources for a particular CultureInfo. This is
695 // not useful for most users of the ResourceManager - call
696 // GetString() or GetObject() instead.
698 // The parameters let you control whether the ResourceSet is created
699 // if it hasn't yet been loaded and if parent CultureInfos should be
700 // loaded as well for resource inheritance.
702 [System.Security.SecuritySafeCritical] // auto-generated
703 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
704 public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) {
705 if (null==culture)
706 throw new ArgumentNullException("culture");
707 Contract.EndContractBlock();
709 Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
710 ResourceSet rs;
711 if (localResourceSets != null) {
712 lock (localResourceSets) {
713 if (localResourceSets.TryGetValue(culture.Name, out rs))
714 return rs;
718 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
720 if (UseManifest && culture.HasInvariantCultureName) {
721 string fileName = GetResourceFileName(culture);
722 RuntimeAssembly mainAssembly = (RuntimeAssembly)MainAssembly;
723 Stream stream = mainAssembly.GetManifestResourceStream(_locationInfo, fileName, m_callingAssembly == MainAssembly, ref stackMark);
724 if (createIfNotExists && stream!=null) {
725 rs = ((ManifestBasedResourceGroveler)resourceGroveler).CreateResourceSet(stream, MainAssembly);
726 AddResourceSet(localResourceSets, culture.Name, ref rs);
727 return rs;
731 // Note: ideally we could plumb through the stack crawl mark here, but we must
732 // call the virtual 3-argument InternalGetResourceSet method for compatibility.
733 // Security-wise, we're not overly interested in protecting access to resources,
734 // since full-trust callers can get them already and most resources are public.
735 // Also, the JIT inliner could always inline a caller into another assembly's
736 // method, so the entire idea of a security check written this way is ----.
737 // So if we happen to return some resources in cases where we should really be
738 // doing a demand for member access permissions, we're not overly concerned.
739 // <STRIP>I verified this with our CAS expert. -- Brian, 2/11/2010</STRIP>
740 return InternalGetResourceSet(culture, createIfNotExists, tryParents);
743 // InternalGetResourceSet is a non-threadsafe method where all the logic
744 // for getting a resource set lives. Access to it is controlled by
745 // threadsafe methods such as GetResourceSet, GetString, & GetObject.
746 // This will take a minimal number of locks.
747 [System.Security.SecuritySafeCritical] // auto-generated
748 [ResourceExposure(ResourceScope.None)]
749 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
750 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
751 protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
753 Contract.Assert(culture != null, "culture != null");
755 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
756 return InternalGetResourceSet(culture,createIfNotExists,tryParents, ref stackMark);
759 // InternalGetResourceSet is a non-threadsafe method where all the logic
760 // for getting a resource set lives. Access to it is controlled by
761 // threadsafe methods such as GetResourceSet, GetString, & GetObject.
762 // This will take a minimal number of locks.
763 [System.Security.SecurityCritical]
764 [ResourceExposure(ResourceScope.None)]
765 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
766 private ResourceSet InternalGetResourceSet(CultureInfo requestedCulture, bool createIfNotExists, bool tryParents, ref StackCrawlMark stackMark)
768 Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
769 ResourceSet rs = null;
770 CultureInfo foundCulture = null;
771 lock (localResourceSets) {
772 if (localResourceSets.TryGetValue(requestedCulture.Name, out rs)) {
773 #if !FEATURE_CORECLR && !MONO
774 if (FrameworkEventSource.IsInitialized) {
775 FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, requestedCulture.Name);
777 #endif
778 return rs;
782 ResourceFallbackManager mgr = new ResourceFallbackManager(requestedCulture, _neutralResourcesCulture, tryParents);
784 foreach (CultureInfo currentCultureInfo in mgr)
786 #if !FEATURE_CORECLR && !MONO
787 if (FrameworkEventSource.IsInitialized)
789 FrameworkEventSource.Log.ResourceManagerLookingForResourceSet(BaseNameField, MainAssembly, currentCultureInfo.Name);
791 #endif
792 lock(localResourceSets) {
793 if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs)) {
794 #if !FEATURE_CORECLR && !MONO
795 if (FrameworkEventSource.IsInitialized)
797 FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, currentCultureInfo.Name);
799 #endif
800 // we need to update the cache if we fellback
801 if(requestedCulture != currentCultureInfo) foundCulture = currentCultureInfo;
802 break;
806 // InternalGetResourceSet will never be threadsafe. However, it must
807 // be protected against reentrancy from the SAME THREAD. (ie, calling
808 // GetSatelliteAssembly may send some window messages or trigger the
809 // Assembly load event, which could fail then call back into the
810 // ResourceManager). It's happened.
812 rs = resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets,
813 tryParents, createIfNotExists, ref stackMark);
815 // found a ResourceSet; we're done
816 if (rs != null)
818 foundCulture = currentCultureInfo;
819 break;
824 if (rs != null && foundCulture != null)
826 // add entries to the cache for the cultures we have gone through
828 // currentCultureInfo now refers to the culture that had resources.
829 // update cultures starting from requested culture up to the culture
830 // that had resources.
831 foreach (CultureInfo updateCultureInfo in mgr)
833 AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);
835 // stop when we've added current or reached invariant (top of chain)
836 if (updateCultureInfo == foundCulture)
838 break;
843 return rs;
846 // Simple helper to ease maintenance and improve readability.
847 private static void AddResourceSet(Dictionary<String,ResourceSet> localResourceSets, String cultureName, ref ResourceSet rs)
849 // InternalGetResourceSet is both recursive and reentrant -
850 // assembly load callbacks in particular are a way we can call
851 // back into the ResourceManager in unexpectedly on the same thread.
852 lock(localResourceSets) {
853 // If another thread added this culture, return that.
854 ResourceSet lostRace;
855 if (localResourceSets.TryGetValue(cultureName, out lostRace)) {
856 if (!Object.ReferenceEquals(lostRace, rs)) {
857 // Note: In certain cases, we can be trying to add a ResourceSet for multiple
858 // cultures on one thread, while a second thread added another ResourceSet for one
859 // of those cultures. So when we lose the ----, we must make sure our ResourceSet
860 // isn't in our dictionary before closing it.
861 if (!localResourceSets.ContainsValue(rs))
862 rs.Dispose();
863 rs = lostRace;
866 else {
867 localResourceSets.Add(cultureName, rs);
872 protected static Version GetSatelliteContractVersion(Assembly a)
874 // Ensure that the assembly reference is not null
875 if (a == null) {
876 throw new ArgumentNullException("a", Environment.GetResourceString("ArgumentNull_Assembly"));
878 Contract.EndContractBlock();
880 #if !FEATURE_WINDOWSPHONE
881 String v = null;
882 if (a.ReflectionOnly) {
883 foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(a)) {
884 if (data.Constructor.DeclaringType == typeof(SatelliteContractVersionAttribute)) {
885 v = (String)data.ConstructorArguments[0].Value;
886 break;
890 if (v == null)
891 return null;
893 else {
894 Object[] attrs = a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
895 if (attrs.Length == 0)
896 return null;
897 Contract.Assert(attrs.Length == 1, "Cannot have multiple instances of SatelliteContractVersionAttribute on an assembly!");
898 v = ((SatelliteContractVersionAttribute)attrs[0]).Version;
900 Version ver;
901 try {
902 ver = new Version(v);
904 catch(ArgumentOutOfRangeException e) {
905 // Note we are prone to hitting infinite loops if mscorlib's
906 // SatelliteContractVersionAttribute contains bogus values.
907 // If this assert fires, please fix the build process for the
908 // BCL directory.
909 if (a == typeof(Object).Assembly) {
910 Contract.Assert(false, "mscorlib's SatelliteContractVersionAttribute is a malformed version string!");
911 return null;
914 throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSatelliteContract_Asm_Ver", a.ToString(), v), e);
916 return ver;
917 #else
918 // On the phone return null. The calling code will use the assembly version instead to avoid potential type
919 // and library loads caused by CA lookup. NetCF uses the assembly version always.
920 return null;
921 #endif
924 [System.Security.SecuritySafeCritical] // auto-generated
925 protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
927 // This method should be obsolete - replace it with the one below.
928 // Unfortunately, we made it protected.
929 UltimateResourceFallbackLocation ignoringUsefulData = UltimateResourceFallbackLocation.MainAssembly;
930 CultureInfo culture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, ref ignoringUsefulData);
931 return culture;
934 // IGNORES VERSION
935 internal static bool CompareNames(String asmTypeName1,
936 String typeName2,
937 AssemblyName asmName2)
939 Contract.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null");
941 // First, compare type names
942 int comma = asmTypeName1.IndexOf(',');
943 if (((comma == -1) ? asmTypeName1.Length : comma) != typeName2.Length)
944 return false;
946 // case sensitive
947 if (String.Compare(asmTypeName1, 0, typeName2, 0, typeName2.Length, StringComparison.Ordinal) != 0)
948 return false;
949 if (comma == -1)
950 return true;
952 // Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
953 // also, for mscorlib ignores everything, since that's what the binder is going to do
954 while(Char.IsWhiteSpace(asmTypeName1[++comma]));
956 // case insensitive
957 AssemblyName an1 = new AssemblyName(asmTypeName1.Substring(comma));
958 if (String.Compare(an1.Name, asmName2.Name, StringComparison.OrdinalIgnoreCase) != 0)
959 return false;
961 // to match IsMscorlib() in VM
962 if (String.Compare(an1.Name, "mscorlib", StringComparison.OrdinalIgnoreCase) == 0)
963 return true;
966 if ((an1.CultureInfo != null) && (asmName2.CultureInfo != null) &&
967 #if FEATURE_USE_LCID
968 (an1.CultureInfo.LCID != asmName2.CultureInfo.LCID)
969 #else
970 (an1.CultureInfo.Name != asmName2.CultureInfo.Name)
971 #endif
973 return false;
975 byte[] pkt1 = an1.GetPublicKeyToken();
976 byte[] pkt2 = asmName2.GetPublicKeyToken();
977 if ((pkt1 != null) && (pkt2 != null)) {
978 if (pkt1.Length != pkt2.Length)
979 return false;
981 for(int i=0; i < pkt1.Length; i++) {
982 if(pkt1[i] != pkt2[i])
983 return false;
987 return true;
990 #if FEATURE_APPX
991 [SecuritySafeCritical]
992 // Throws WinRT hresults
993 private string GetStringFromPRI(String stringName, String startingCulture, String neutralResourcesCulture) {
994 Contract.Assert(_bUsingModernResourceManagement);
995 Contract.Assert(_WinRTResourceManager != null);
996 Contract.Assert(_PRIonAppXInitialized);
997 Contract.Assert(AppDomain.IsAppXModel());
999 if (stringName.Length == 0)
1000 return null;
1002 string resourceString = null;
1004 // Do not handle exceptions. See the comment in SetAppXConfiguration about throwing
1005 // exception types that the ResourceManager class is not documented to throw.
1006 resourceString = _WinRTResourceManager.GetString(
1007 stringName,
1008 String.IsNullOrEmpty(startingCulture) ? null : startingCulture,
1009 String.IsNullOrEmpty(neutralResourcesCulture) ? null : neutralResourcesCulture);
1011 return resourceString;
1014 // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the type via reflection.
1015 // It would be better if we could just implement WindowsRuntimeResourceManager in mscorlib, but we can't, because
1016 // we can do very little with WinRT in mscorlib.
1017 [SecurityCritical]
1018 internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
1020 Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
1021 return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
1023 #endif
1025 [NonSerialized]
1026 private bool _bUsingModernResourceManagement; // Written only by SetAppXConfiguration
1028 #if FEATURE_APPX
1029 [NonSerialized]
1030 [SecurityCritical]
1031 private WindowsRuntimeResourceManagerBase _WinRTResourceManager; // Written only by SetAppXConfiguration
1033 [NonSerialized]
1034 private bool _PRIonAppXInitialized; // Written only by SetAppXConfiguration
1036 [NonSerialized]
1037 private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration
1039 [SecuritySafeCritical]
1040 #endif // FEATURE_APPX
1041 // Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
1042 // Throws MissingManifestResourceException and WinRT HResults
1044 private void SetAppXConfiguration()
1046 Contract.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member
1047 #if FEATURE_APPX
1048 Contract.Assert(_WinRTResourceManager == null); // Only this function writes to this member
1049 Contract.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
1050 Contract.Assert(_PRIExceptionInfo == null); // Only this function writes to this member
1052 bool bUsingSatelliteAssembliesUnderAppX = false;
1054 RuntimeAssembly resourcesAssembly = (RuntimeAssembly)MainAssembly;
1056 if (resourcesAssembly == null)
1057 resourcesAssembly = m_callingAssembly;
1059 if (resourcesAssembly != null)
1061 if (resourcesAssembly != typeof(Object).Assembly) // We are not loading resources for mscorlib
1063 // Cannot load the WindowsRuntimeResourceManager when in a compilation process, since it
1064 // lives in System.Runtime.WindowsRuntime and only mscorlib may be loaded for execution.
1065 if (AppDomain.IsAppXModel() && !AppDomain.IsAppXNGen)
1067 s_IsAppXModel = true;
1069 // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField.
1070 String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName;
1072 // The only way this can happen is if a class inherited from ResourceManager and
1073 // did not set the BaseNameField before calling the protected ResourceManager() constructor.
1074 // For other constructors, we would already have thrown an ArgumentNullException by now.
1075 // Throwing an ArgumentNullException now is not the right thing to do because technically
1076 // ResourceManager() takes no arguments, and because it is not documented as throwing
1077 // any exceptions. Instead, let's go through the rest of the initialization with this set to
1078 // an empty string. We may in fact fail earlier for another reason, but otherwise we will
1079 // throw a MissingManifestResourceException when GetString is called indicating that a
1080 // resW filename called "" could not be found.
1081 if (reswFilename == null)
1082 reswFilename = String.Empty;
1084 WindowsRuntimeResourceManagerBase WRRM = null;
1085 bool bWRRM_Initialized = false;
1087 if (AppDomain.IsAppXDesignMode())
1089 WRRM = GetWinRTResourceManager();
1090 try {
1091 PRIExceptionInfo exceptionInfo; // If the exception info is filled in, we will ignore it.
1092 bWRRM_Initialized = WRRM.Initialize(resourcesAssembly.Location, reswFilename, out exceptionInfo);
1093 bUsingSatelliteAssembliesUnderAppX = !bWRRM_Initialized;
1095 catch(Exception e)
1097 bUsingSatelliteAssembliesUnderAppX = true;
1098 if (e.IsTransient)
1099 throw;
1103 if (!bUsingSatelliteAssembliesUnderAppX)
1105 // See AssemblyNative::IsFrameworkAssembly for details on which kinds of assemblies are considered Framework assemblies.
1106 // The Modern Resource Manager is not used for such assemblies - they continue to use satellite assemblies (i.e. .resources.dll files).
1107 _bUsingModernResourceManagement = !resourcesAssembly.IsFrameworkAssembly();
1109 if (_bUsingModernResourceManagement)
1111 // Only now are we certain that we need the PRI file.
1113 // Note that if IsAppXDesignMode is false, we haven't checked if the PRI file exists.
1114 // This is by design. We will find out in the call to WindowsRuntimeResourceManager.Initialize below.
1116 // At this point it is important NOT to set _bUsingModernResourceManagement to false
1117 // if the PRI file does not exist because we are now certain we need to load PRI
1118 // resources. We want to fail by throwing a MissingManifestResourceException
1119 // if WindowsRuntimeResourceManager.Initialize fails to locate the PRI file. We do not
1120 // want to fall back to using satellite assemblies anymore. Note that we would not throw
1121 // the MissingManifestResourceException from this function, but from GetString. See the
1122 // comment below on the reason for this.
1124 if (WRRM != null && bWRRM_Initialized)
1126 // Reuse the one successfully created earlier
1127 _WinRTResourceManager = WRRM;
1128 _PRIonAppXInitialized = true;
1130 else
1132 _WinRTResourceManager = GetWinRTResourceManager();
1134 try {
1135 _PRIonAppXInitialized = _WinRTResourceManager.Initialize(resourcesAssembly.Location, reswFilename, out _PRIExceptionInfo);
1137 // Note that _PRIExceptionInfo might be null - this is OK.
1138 // In that case we will just throw the generic
1139 // MissingManifestResource_NoPRIresources exception.
1140 // See the implementation of GetString for more details.
1142 // We would like to be able to throw a MissingManifestResourceException here if PRI resources
1143 // could not be loaded for a recognized reason. However, the ResourceManager constructors
1144 // that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException,
1145 // and since they are part of the portable profile, we cannot start throwing a new exception type
1146 // as that would break existing portable libraries. Hence we must save the exception information
1147 // now and throw the exception on the first call to GetString.
1148 catch(FileNotFoundException)
1150 // We will throw MissingManifestResource_NoPRIresources from GetString
1151 // when we see that _PRIonAppXInitialized is false.
1153 catch(Exception e)
1155 // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps
1156 // in WindowsRuntimeResourceManager.Initialize.
1157 // In this case _PRIExceptionInfo is now null and we will just throw the generic
1158 // MissingManifestResource_NoPRIresources exception.
1159 // See the implementation of GetString for more details.
1160 if (e.HResult != __HResults.ERROR_MRM_MAP_NOT_FOUND)
1161 throw; // Unexpected exception code. Bubble it up to the caller.
1163 // Allow all other exception types to bubble up to the caller.
1165 // Yes, this causes us to potentially throw exception types that are not documented.
1167 // Ultimately the tradeoff is the following:
1168 // -We could ignore unknown exceptions or rethrow them as inner exceptions
1169 // of exceptions that the ResourceManager class is already documented as throwing.
1170 // This would allow existing portable libraries to gracefully recover if they don't care
1171 // too much about the ResourceManager object they are using. However it could
1172 // mask potentially fatal errors that we are not aware of, such as a disk drive failing.
1175 // The alternative, which we chose, is to throw unknown exceptions. This may tear
1176 // down the process if the portable library and app don't expect this exception type.
1177 // On the other hand, this won't mask potentially fatal errors we don't know about.
1184 // resourcesAssembly == null should not happen but it can. See the comment on Assembly.GetCallingAssembly.
1185 // However for the sake of 100% backwards compatibility on Win7 and below, we must leave
1186 // _bUsingModernResourceManagement as false.
1187 #endif // FEATURE_APPX
1190 // Looks up a resource value for a particular name. Looks in the
1191 // current thread's CultureInfo, and if not found, all parent CultureInfos.
1192 // Returns null if the resource wasn't found.
1194 public virtual String GetString(String name) {
1195 return GetString(name, (CultureInfo)null);
1198 // Looks up a resource value for a particular name. Looks in the
1199 // specified CultureInfo, and if not found, all parent CultureInfos.
1200 // Returns null if the resource wasn't found.
1202 public virtual String GetString(String name, CultureInfo culture) {
1203 if (null==name)
1204 throw new ArgumentNullException("name");
1205 Contract.EndContractBlock();
1207 #if FEATURE_APPX
1208 if(s_IsAppXModel)
1210 // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
1211 // null it out, so that we re-compute it. If we use modern resource lookup, we may end up getting a "better"
1212 // match, since CultureInfo objects can't represent all the different languages the AppX resource model supports.
1213 // For classic resources, this causes us to ignore the languages list and instead use the older Win32 behavior,
1214 // which is the design choice we've made. (See the call a little later to GetCurrentUICultureNoAppX()).
1215 if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
1217 culture = null;
1221 if (_bUsingModernResourceManagement)
1223 if (_PRIonAppXInitialized == false)
1225 // Always throw if we did not fully succeed in initializing the WinRT Resource Manager.
1227 if (_PRIExceptionInfo != null && _PRIExceptionInfo._PackageSimpleName != null && _PRIExceptionInfo._ResWFile != null)
1228 throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_ResWFileNotLoaded", _PRIExceptionInfo._ResWFile, _PRIExceptionInfo._PackageSimpleName));
1230 throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoPRIresources"));
1233 // Throws WinRT hresults.
1234 return GetStringFromPRI(name,
1235 culture == null ? null : culture.Name,
1236 _neutralResourcesCulture.Name);
1238 else
1239 #endif // FEATURE_APPX
1241 if (null==culture) {
1242 // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
1243 // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
1244 culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
1247 #if !FEATURE_CORECLR && !MONO
1248 if (FrameworkEventSource.IsInitialized)
1250 FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
1252 #endif
1253 ResourceSet last = GetFirstResourceSet(culture);
1255 if (last != null)
1257 String value = last.GetString(name, _ignoreCase);
1258 if (value != null)
1259 return value;
1263 // This is the CultureInfo hierarchy traversal code for resource
1264 // lookups, similar but necessarily orthogonal to the ResourceSet
1265 // lookup logic.
1266 ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
1267 foreach (CultureInfo currentCultureInfo in mgr) {
1269 ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
1270 if (rs == null)
1271 break;
1273 if (rs != last) {
1274 String value = rs.GetString(name, _ignoreCase);
1275 if (value != null)
1277 // update last used ResourceSet
1278 if (_lastUsedResourceCache != null) {
1279 lock (_lastUsedResourceCache) {
1280 _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
1281 _lastUsedResourceCache.lastResourceSet = rs;
1284 return value;
1287 last = rs;
1291 #if !FEATURE_CORECLR && !MONO
1292 if (FrameworkEventSource.IsInitialized)
1294 FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
1296 #endif
1299 return null;
1303 // Looks up a resource value for a particular name. Looks in the
1304 // current thread's CultureInfo, and if not found, all parent CultureInfos.
1305 // Returns null if the resource wasn't found.
1307 public virtual Object GetObject(String name) {
1308 return GetObject(name, (CultureInfo)null, true);
1311 // Looks up a resource value for a particular name. Looks in the
1312 // specified CultureInfo, and if not found, all parent CultureInfos.
1313 // Returns null if the resource wasn't found.
1314 public virtual Object GetObject(String name, CultureInfo culture) {
1315 return GetObject(name, culture, true);
1318 private Object GetObject(String name, CultureInfo culture, bool wrapUnmanagedMemStream)
1320 if (null==name)
1321 throw new ArgumentNullException("name");
1322 Contract.EndContractBlock();
1324 #if !FEATURE_CORECLR
1325 if(s_IsAppXModel)
1327 // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
1328 // null it out, so that we re-compute it based on the Win32 value and not the AppX language list value.
1329 // (See the call a little later to GetCurrentUICultureNoAppX()).
1330 if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
1332 culture = null;
1335 #endif
1337 if (null==culture) {
1338 // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
1339 // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
1340 culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
1343 #if !FEATURE_CORECLR && !MONO
1344 if (FrameworkEventSource.IsInitialized)
1346 FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
1348 #endif
1349 ResourceSet last = GetFirstResourceSet(culture);
1350 if (last != null)
1352 Object value = last.GetObject(name, _ignoreCase);
1354 if (value != null)
1356 UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
1357 if (stream != null && wrapUnmanagedMemStream)
1358 return new UnmanagedMemoryStreamWrapper(stream);
1359 else
1360 return value;
1364 // This is the CultureInfo hierarchy traversal code for resource
1365 // lookups, similar but necessarily orthogonal to the ResourceSet
1366 // lookup logic.
1367 ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
1369 foreach (CultureInfo currentCultureInfo in mgr) {
1370 // Note: Technically this method should be passed in a stack crawl mark that we then pass
1371 // to InternalGetResourceSet for ensuring we demand permissions to read your private resources
1372 // if you're reading resources from an assembly other than yourself. But, we must call our
1373 // three argument overload (without the stack crawl mark) for compatibility. After
1374 // consideration, we aren't worried about the security impact.
1375 ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
1376 if (rs == null)
1377 break;
1379 if (rs != last) {
1380 Object value = rs.GetObject(name, _ignoreCase);
1381 if (value != null) {
1382 // update the last used ResourceSet
1383 if (_lastUsedResourceCache != null) {
1384 lock (_lastUsedResourceCache) {
1385 _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
1386 _lastUsedResourceCache.lastResourceSet = rs;
1390 UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
1391 if (stream != null && wrapUnmanagedMemStream)
1392 return new UnmanagedMemoryStreamWrapper(stream);
1393 else
1394 return value;
1397 last = rs;
1401 #if !FEATURE_CORECLR && !MONO
1402 if (FrameworkEventSource.IsInitialized)
1404 FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
1406 #endif
1407 return null;
1410 [ComVisible(false)]
1411 public UnmanagedMemoryStream GetStream(String name) {
1412 return GetStream(name, (CultureInfo)null);
1415 [ComVisible(false)]
1416 public UnmanagedMemoryStream GetStream(String name, CultureInfo culture) {
1417 Object obj = GetObject(name, culture, false);
1418 UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
1419 if (ums == null && obj != null)
1420 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotStream_Name", name));
1421 return ums;
1424 #if RESOURCE_SATELLITE_CONFIG
1425 // Internal helper method - gives an end user the ability to prevent
1426 // satellite assembly probes for certain cultures via a config file.
1427 [System.Security.SecurityCritical] // auto-generated
1428 private bool TryLookingForSatellite(CultureInfo lookForCulture)
1430 if (!_checkedConfigFile) {
1431 lock (this) {
1432 if (!_checkedConfigFile) {
1433 _checkedConfigFile = true;
1434 _installedSatelliteInfo = GetSatelliteAssembliesFromConfig();
1439 if (_installedSatelliteInfo == null)
1440 return true;
1442 String[] installedSatellites = (String[]) _installedSatelliteInfo[MainAssembly.FullName];
1444 if (installedSatellites == null)
1445 return true;
1447 // The config file told us what satellites might be installed.
1448 int pos = Array.IndexOf(installedSatellites, lookForCulture.Name);
1450 #if !FEATURE_CORECLR && !MONO
1451 if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
1452 if (pos < 0) {
1453 FrameworkEventSource.Log.ResourceManagerCultureNotFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
1455 else {
1456 FrameworkEventSource.Log.ResourceManagerCultureFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
1459 #endif
1460 return pos >= 0;
1463 // Note: There is one config file per appdomain. This is not
1464 // per-process nor per-assembly.
1465 [System.Security.SecurityCritical] // auto-generated
1466 [ResourceExposure(ResourceScope.None)]
1467 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1468 private Hashtable GetSatelliteAssembliesFromConfig()
1470 #if FEATURE_FUSION
1472 String fileName = AppDomain.CurrentDomain.FusionStore.ConfigurationFileInternal;
1473 if (fileName == null) {
1474 return null;
1477 // Don't do a security assert. We need to support semi-trusted
1478 // scenarios, but asserting here causes infinite resource lookups
1479 // while initializing security & looking up mscorlib's config file.
1480 // Use internal methods to bypass security checks.
1482 // If we're dealing with a local file name or a UNC path instead
1483 // of a URL, check to see if the file exists here for perf (avoids
1484 // throwing a FileNotFoundException).
1485 if (fileName.Length >= 2 &&
1486 ((fileName[1] == Path.VolumeSeparatorChar) || (fileName[0] == Path.DirectorySeparatorChar && fileName[1] == Path.DirectorySeparatorChar)) &&
1487 !File.InternalExists(fileName))
1488 return null;
1490 ConfigTreeParser parser = new ConfigTreeParser();
1491 String queryPath = "/configuration/satelliteassemblies";
1492 ConfigNode node = null;
1493 // Catch exceptions in case a web app doesn't have a config file.
1494 try {
1495 node = parser.Parse(fileName, queryPath, true);
1497 catch(Exception) {}
1499 if (node == null) {
1500 return null;
1503 // The application config file will contain sections like this:
1504 // <?xml version="1.0"?>
1505 // <configuration>
1506 // <satelliteassemblies>
1507 // <assembly name="mscorlib, Version=..., PublicKeyToken=...">
1508 // <culture>fr</culture>
1509 // </assembly>
1510 // <assembly name="UserAssembly, ...">
1511 // <culture>fr-FR</culture>
1512 // <culture>de-CH</culture>
1513 // </assembly>
1514 // <assembly name="UserAssembly2, ...">
1515 // </assembly>
1516 // </satelliteassemblies>
1517 // </configuration>
1518 Hashtable satelliteInfo = new Hashtable(StringComparer.OrdinalIgnoreCase);
1519 foreach(ConfigNode assemblyNode in node.Children) {
1520 if (!String.Equals(assemblyNode.Name, "assembly"))
1521 throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTag", Path.GetFileName(fileName), assemblyNode.Name));
1523 if (assemblyNode.Attributes.Count == 0)
1524 throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagNoAttr", Path.GetFileName(fileName)));
1526 DictionaryEntry de = (DictionaryEntry) assemblyNode.Attributes[0];
1527 String assemblyName = (String) de.Value;
1528 if (!String.Equals(de.Key, "name") || String.IsNullOrEmpty(assemblyName) || assemblyNode.Attributes.Count > 1)
1529 throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagBadAttr", Path.GetFileName(fileName), de.Key, de.Value));
1531 ArrayList list = new ArrayList(5);
1532 foreach(ConfigNode child in assemblyNode.Children)
1533 if (child.Value != null)
1534 list.Add(child.Value);
1536 String[] satellites = new String[list.Count];
1537 for(int i=0; i<satellites.Length; i++) {
1538 String cultureName = (String)list[i];
1539 satellites[i] = cultureName;
1540 #if !FEATURE_CORECLR && !MONO
1541 if (FrameworkEventSource.IsInitialized)
1543 FrameworkEventSource.Log.ResourceManagerAddingCultureFromConfigFile(BaseNameField, MainAssembly, cultureName);
1545 #endif
1548 satelliteInfo.Add(assemblyName, satellites);
1551 return satelliteInfo;
1552 #else
1553 return null;
1554 #endif //FEATURE_FUSION
1557 #endif // RESOURCE_SATELLITE_CONFIG
1559 internal class ResourceManagerMediator
1561 private ResourceManager _rm;
1563 internal ResourceManagerMediator(ResourceManager rm)
1565 if (rm == null)
1567 throw new ArgumentNullException("rm");
1569 _rm = rm;
1572 // NEEDED ONLY BY FILE-BASED
1573 internal String ModuleDir
1575 get { return _rm.moduleDir; }
1578 // NEEDED BOTH BY FILE-BASED AND ----Y-BASED
1579 internal Type LocationInfo
1581 get { return _rm._locationInfo; }
1584 internal Type UserResourceSet
1586 get { return _rm._userResourceSet; }
1589 internal String BaseNameField
1591 get { return _rm.BaseNameField; }
1594 internal CultureInfo NeutralResourcesCulture
1596 get { return _rm._neutralResourcesCulture; }
1597 set { _rm._neutralResourcesCulture = value; }
1600 internal String GetResourceFileName(CultureInfo culture)
1602 return _rm.GetResourceFileName(culture);
1605 // NEEDED ONLY BY ----Y-BASED
1606 internal bool LookedForSatelliteContractVersion
1608 get { return _rm._lookedForSatelliteContractVersion; }
1609 set { _rm._lookedForSatelliteContractVersion = value; }
1612 internal Version SatelliteContractVersion
1614 get { return _rm._satelliteContractVersion; }
1615 set { _rm._satelliteContractVersion = value; }
1618 internal Version ObtainSatelliteContractVersion(Assembly a)
1620 return ResourceManager.GetSatelliteContractVersion(a);
1623 internal UltimateResourceFallbackLocation FallbackLoc
1625 get { return _rm.FallbackLocation; }
1626 set { _rm._fallbackLoc = value; }
1629 internal RuntimeAssembly CallingAssembly
1631 get { return _rm.m_callingAssembly; }
1634 #if FEATURE_SPLIT_RESOURCES
1635 internal bool IsDebugSatellite
1637 get { return _rm._isDebugSatellite; }
1639 #endif
1641 internal RuntimeAssembly MainAssembly
1643 get { return (RuntimeAssembly)_rm.MainAssembly; }
1646 // this is weird because we have BaseNameField accessor above, but we're sticking
1647 // with it for compat.
1648 internal String BaseName
1650 get { return _rm.BaseName; }
1654 #if RESOURCE_SATELLITE_CONFIG
1655 [System.Security.SecurityCritical] // auto-generated
1656 internal bool TryLookingForSatellite(CultureInfo lookForCulture)
1658 return _rm.TryLookingForSatellite(lookForCulture);
1660 #endif