1706 lines
80 KiB
C#
1706 lines
80 KiB
C#
// ==++==
|
||
//
|
||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
//
|
||
// ==--==
|
||
/*============================================================
|
||
**
|
||
** Class: ResourceManager
|
||
**
|
||
** <OWNER>jathaine</OWNER>
|
||
**
|
||
**
|
||
** Purpose: Default way to access String and Object resources
|
||
** from an assembly.
|
||
**
|
||
**
|
||
===========================================================*/
|
||
|
||
namespace System.Resources {
|
||
using System;
|
||
using System.IO;
|
||
using System.Globalization;
|
||
using System.Collections;
|
||
using System.Text;
|
||
using System.Reflection;
|
||
using System.Runtime.Serialization;
|
||
using System.Security;
|
||
using System.Security.Permissions;
|
||
using System.Threading;
|
||
using System.Runtime.InteropServices;
|
||
using System.Runtime.CompilerServices;
|
||
using Microsoft.Win32;
|
||
using System.Collections.Generic;
|
||
using System.Runtime.Versioning;
|
||
using System.Diagnostics.Contracts;
|
||
#if !FEATURE_CORECLR
|
||
using System.Diagnostics.Tracing;
|
||
#endif
|
||
|
||
#if FEATURE_APPX
|
||
//
|
||
// This is implemented in System.Runtime.WindowsRuntime as function System.Resources.WindowsRuntimeResourceManager,
|
||
// allowing us to ask for a WinRT-specific ResourceManager.
|
||
// It is important to have WindowsRuntimeResourceManagerBase as regular class with virtual methods and default implementations.
|
||
// Defining WindowsRuntimeResourceManagerBase as abstract class or interface will cause issues when adding more methods to it
|
||
// because it<69>ll create dependency between mscorlib and System.Runtime.WindowsRuntime which will require always shipping both DLLs together.
|
||
// Also using interface or abstract class will not play nice with FriendAccessAllowed.
|
||
//
|
||
[FriendAccessAllowed]
|
||
[SecurityCritical]
|
||
internal class WindowsRuntimeResourceManagerBase
|
||
{
|
||
[SecurityCritical]
|
||
public virtual bool Initialize(string libpath, string reswFilename, out PRIExceptionInfo exceptionInfo){exceptionInfo = null; return false;}
|
||
|
||
[SecurityCritical]
|
||
public virtual String GetString(String stringName, String startingCulture, String neutralResourcesCulture){return null;}
|
||
|
||
public virtual CultureInfo GlobalResourceContextBestFitCultureInfo {
|
||
[SecurityCritical]
|
||
get { return null; }
|
||
}
|
||
|
||
[SecurityCritical]
|
||
public virtual bool SetGlobalResourceContextDefaultCulture(CultureInfo ci) { return false; }
|
||
}
|
||
|
||
[FriendAccessAllowed]
|
||
// [[....] 3/9/2012] This class should be named PRIErrorInfo.
|
||
//
|
||
// During Dev11 CLR RC Ask mode, the Windows Modern Resource Manager
|
||
// made a breaking change such that ResourceMap.GetSubtree returns null when a subtree is
|
||
// not found instead of throwing an exception. As a result the name of this class is no longer accurate.
|
||
// It should be called PRIErrorInfo. However changing the name of this internal class would cause
|
||
// mscorlib.asmmeta and System.Runtime.WindowsRuntime.asmmeta to change,
|
||
// which would in turn require updating of the mscorlib and System.Runtime.WindowsRuntime
|
||
// reference assemblies under InternalApis. This would not meet the Ask Mode bar at this time.
|
||
// To get an idea of which files may need to be updated when updating this name,
|
||
// see changeset 399234 in the DevDiv2 database, though the update procedure may have changed
|
||
// by the time you read this.
|
||
internal class PRIExceptionInfo
|
||
{
|
||
public string _PackageSimpleName;
|
||
public string _ResWFile;
|
||
}
|
||
#endif // FEATURE_APPX
|
||
|
||
// Resource Manager exposes an assembly's resources to an application for
|
||
// the correct CultureInfo. An example would be localizing text for a
|
||
// user-visible message. Create a set of resource files listing a name
|
||
// for a message and its value, compile them using ResGen, put them in
|
||
// an appropriate place (your assembly manifest(?)), then create a Resource
|
||
// Manager and query for the name of the message you want. The Resource
|
||
// Manager will use CultureInfo.GetCurrentUICulture() to look
|
||
// up a resource for your user's locale settings.
|
||
//
|
||
// Users should ideally create a resource file for every culture, or
|
||
// at least a meaningful subset. The filenames will follow the naming
|
||
// scheme:
|
||
//
|
||
// basename.culture name.resources
|
||
//
|
||
// The base name can be the name of your application, or depending on
|
||
// the granularity desired, possibly the name of each class. The culture
|
||
// name is determined from CultureInfo's Name property.
|
||
// An example file name may be MyApp.en-US.resources for
|
||
// MyApp's US English resources.
|
||
//
|
||
// -----------------
|
||
// Refactoring Notes
|
||
// -----------------
|
||
// In Feb 08, began first step of refactoring ResourceManager to improve
|
||
// maintainability (sd changelist 3012100). This resulted in breaking
|
||
// apart the InternalGetResourceSet "big loop" so that the file-based
|
||
// and manifest-based lookup was located in separate methods.
|
||
// In Apr 08, continued refactoring so that file-based and manifest-based
|
||
// concerns are encapsulated by separate classes. At construction, the
|
||
// ResourceManager creates one of these classes based on whether the
|
||
// RM will need to use file-based or manifest-based resources, and
|
||
// afterwards refers to this through the interface IResourceGroveler.
|
||
//
|
||
// Serialization Compat: Ideally, we could have refactored further but
|
||
// this would have broken serialization compat. For example, the
|
||
// ResourceManager member UseManifest and UseSatelliteAssem are no
|
||
// longer relevant on ResourceManager. Similarly, other members could
|
||
// ideally be moved to the file-based or manifest-based classes
|
||
// because they are only relevant for those types of lookup.
|
||
//
|
||
// Solution now / in the future:
|
||
// For now, we simply use a mediator class so that we can keep these
|
||
// members on ResourceManager but allow the file-based and manifest-
|
||
// based classes to access/set these members in a uniform way. See
|
||
// ResourceManagerMediator.
|
||
// We encapsulate fallback logic in a fallback iterator class, so that
|
||
// this logic isn't duplicated in several methods.
|
||
//
|
||
// In the future, we can look into either breaking serialization if we
|
||
// decide this doesn't make sense for ResourceManager (i.e. how common
|
||
// is the scenario), manually make serialization work by providing
|
||
// appropriate OnSerialization, Deserialization methods. We can also
|
||
// look into further factoring and better design of IResourceGroveler
|
||
// interface to accommodate unused parameters that don't make sense
|
||
// for either file-based or manifest-based lookup paths.
|
||
//
|
||
// Benefits of this refactoring:
|
||
// - Makes it possible to understand what the ResourceManager does,
|
||
// which is key for maintainability.
|
||
// - Makes the ResourceManager more extensible by identifying and
|
||
// encapsulating what varies
|
||
// - Unearthed a bug that's been lurking a while in file-based
|
||
// lookup paths for InternalGetResourceSet if createIfNotExists is
|
||
// false.
|
||
// - Reuses logic, e.g. by breaking apart the culture fallback into
|
||
// the fallback iterator class, we don't have to repeat the
|
||
// sometimes confusing fallback logic across multiple methods
|
||
// - Fxcop violations reduced to 1/5th of original count. Most
|
||
// importantly, code complexity violations disappeared.
|
||
// - Finally, it got rid of dead code paths. Because the big loop was
|
||
// so confusing, it masked unused chunks of code. Also, dividing
|
||
// between file-based and manifest-based allowed functionaliy
|
||
// unused in silverlight to fall out.
|
||
//
|
||
// Note: this type is integral to the construction of exception objects,
|
||
// and sometimes this has to be done in low memory situtations (OOM) or
|
||
// to create TypeInitializationExceptions due to failure of a static class
|
||
// constructor. This type needs to be extremely careful and assume that
|
||
// any type it references may have previously failed to construct, so statics
|
||
// belonging to that type may not be initialized. FrameworkEventSource.Log
|
||
// is one such example.
|
||
//
|
||
|
||
[Serializable]
|
||
[System.Runtime.InteropServices.ComVisible(true)]
|
||
public class ResourceManager
|
||
{
|
||
|
||
internal class CultureNameResourceSetPair {
|
||
public String lastCultureName;
|
||
public ResourceSet lastResourceSet;
|
||
}
|
||
|
||
protected String BaseNameField;
|
||
// Sets is a many-to-one table of CultureInfos mapped to ResourceSets.
|
||
// Don't synchronize ResourceSets - too fine-grained a lock to be effective
|
||
[Obsolete("call InternalGetResourceSet instead")]
|
||
protected Hashtable ResourceSets;
|
||
|
||
|
||
// don't serialize the cache of ResourceSets
|
||
[NonSerialized]
|
||
private Dictionary <String,ResourceSet> _resourceSets;
|
||
private String moduleDir; // For assembly-ignorant directory location
|
||
protected Assembly MainAssembly; // Need the assembly manifest sometimes.
|
||
private Type _locationInfo; // For Assembly or type-based directory layout
|
||
private Type _userResourceSet; // Which ResourceSet instance to create
|
||
private CultureInfo _neutralResourcesCulture; // For perf optimizations.
|
||
|
||
[NonSerialized]
|
||
private CultureNameResourceSetPair _lastUsedResourceCache;
|
||
|
||
private bool _ignoreCase; // Whether case matters in GetString & GetObject
|
||
|
||
private bool UseManifest; // Use Assembly manifest, or grovel disk.
|
||
|
||
// unused! But need to keep for serialization
|
||
[OptionalField(VersionAdded = 1)]
|
||
private bool UseSatelliteAssem; // Are all the .resources files in the
|
||
// main assembly, or in satellite assemblies for each culture?
|
||
#if RESOURCE_SATELLITE_CONFIG
|
||
private static volatile Hashtable _installedSatelliteInfo; // Give the user the option
|
||
// to prevent certain satellite assembly probes via a config file.
|
||
// Note that config files are per-appdomain, not per-assembly nor process
|
||
private static volatile bool _checkedConfigFile; // Did we read the app's config file?
|
||
#endif
|
||
|
||
// Whether to fall back to the main assembly or a particular
|
||
// satellite for the neutral resources.
|
||
[OptionalField]
|
||
private UltimateResourceFallbackLocation _fallbackLoc;
|
||
// Version number of satellite assemblies to look for. May be null.
|
||
[OptionalField]
|
||
private Version _satelliteContractVersion;
|
||
[OptionalField]
|
||
private bool _lookedForSatelliteContractVersion;
|
||
|
||
// unused! But need to keep for serialization
|
||
[OptionalField(VersionAdded = 1)]
|
||
private Assembly _callingAssembly; // Assembly who created the ResMgr.
|
||
|
||
// replaces _callingAssembly
|
||
[OptionalField(VersionAdded = 4)]
|
||
private RuntimeAssembly m_callingAssembly; // Assembly who created the ResMgr.
|
||
|
||
// no need to serialize this; just create a new one on deserialization
|
||
[NonSerialized]
|
||
private IResourceGroveler resourceGroveler;
|
||
|
||
public static readonly int MagicNumber = unchecked((int)0xBEEFCACE); // If only hex had a K...
|
||
|
||
// Version number so ResMgr can get the ideal set of classes for you.
|
||
// ResMgr header is:
|
||
// 1) MagicNumber (little endian Int32)
|
||
// 2) HeaderVersionNumber (little endian Int32)
|
||
// 3) Num Bytes to skip past ResMgr header (little endian Int32)
|
||
// 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
|
||
// 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
|
||
public static readonly int HeaderVersionNumber = 1;
|
||
|
||
//
|
||
//It would be better if we could use _neutralCulture instead of calling
|
||
//CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor. CultureInfo
|
||
//initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
|
||
//there yet because CultureInfo's class initializer hasn't finished. If we move SystemResMgr off of
|
||
//Assembly (or at least make it an internal property) we should be able to circumvent this problem.
|
||
//
|
||
// private static CultureInfo _neutralCulture = null;
|
||
|
||
// This is our min required ResourceSet type.
|
||
private static readonly Type _minResourceSet = typeof(ResourceSet);
|
||
// These Strings are used to avoid using Reflection in CreateResourceSet.
|
||
// The first set are used by ResourceWriter. The second are used by
|
||
// InternalResGen.
|
||
internal static readonly String ResReaderTypeName = typeof(ResourceReader).FullName;
|
||
internal static readonly String ResSetTypeName = typeof(RuntimeResourceSet).FullName;
|
||
internal static readonly String MscorlibName = typeof(ResourceReader).Assembly.FullName;
|
||
internal const String ResFileExtension = ".resources";
|
||
internal const int ResFileExtensionLength = 10;
|
||
|
||
// My private debugging aid. Set to 5 or 6 for verbose output. Set to 3
|
||
// for summary level information.
|
||
internal static readonly int DEBUG = 0; //Making this const causes C# to consider all of the code that it guards unreachable.
|
||
|
||
private static volatile bool s_IsAppXModel;
|
||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
|
||
private void Init()
|
||
{
|
||
m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
|
||
}
|
||
|
||
protected ResourceManager()
|
||
{
|
||
#if !FEATURE_CORECLR
|
||
// This constructor is not designed to be used under AppX and is not in the Win8 profile.
|
||
// However designers may use them even if they are running under AppX since they are
|
||
// not subject to the restrictions of the Win8 profile.
|
||
Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
|
||
#endif
|
||
|
||
Init();
|
||
|
||
_lastUsedResourceCache = new CultureNameResourceSetPair();
|
||
ResourceManagerMediator mediator = new ResourceManagerMediator(this);
|
||
resourceGroveler = new ManifestBasedResourceGroveler(mediator);
|
||
}
|
||
|
||
// Constructs a Resource Manager for files beginning with
|
||
// baseName in the directory specified by resourceDir
|
||
// or in the current directory. This Assembly-ignorant constructor is
|
||
// mostly useful for testing your own ResourceSet implementation.
|
||
//
|
||
// A good example of a baseName might be "Strings". BaseName
|
||
// should not end in ".resources".
|
||
//
|
||
// Note: System.Windows.Forms uses this method at design time.
|
||
//
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) {
|
||
if (null==baseName)
|
||
throw new ArgumentNullException("baseName");
|
||
if (null==resourceDir)
|
||
throw new ArgumentNullException("resourceDir");
|
||
Contract.EndContractBlock();
|
||
|
||
#if !FEATURE_CORECLR
|
||
// This constructor is not designed to be used under AppX and is not in the Win8 profile.
|
||
// However designers may use them even if they are running under AppX since they are
|
||
// not subject to the restrictions of the Win8 profile.
|
||
Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
|
||
#endif
|
||
|
||
BaseNameField = baseName;
|
||
|
||
moduleDir = resourceDir;
|
||
_userResourceSet = usingResourceSet;
|
||
#pragma warning disable 618
|
||
ResourceSets = new Hashtable(); // for backward compatibility
|
||
#pragma warning restore 618
|
||
_resourceSets = new Dictionary<String, ResourceSet>();
|
||
_lastUsedResourceCache = new CultureNameResourceSetPair();
|
||
UseManifest = false;
|
||
|
||
ResourceManagerMediator mediator = new ResourceManagerMediator(this);
|
||
resourceGroveler = new FileBasedResourceGroveler(mediator);
|
||
|
||
#if !FEATURE_CORECLR && !MONO // PAL doesn't support eventing, and we don't compile event providers for coreclr
|
||
if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
|
||
CultureInfo culture = CultureInfo.InvariantCulture;
|
||
String defaultResName = GetResourceFileName(culture);
|
||
|
||
if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
|
||
FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
|
||
}
|
||
else {
|
||
FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, defaultResName);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
|
||
public ResourceManager(String baseName, Assembly assembly)
|
||
{
|
||
if (null==baseName)
|
||
throw new ArgumentNullException("baseName");
|
||
|
||
if (null==assembly)
|
||
throw new ArgumentNullException("assembly");
|
||
Contract.EndContractBlock();
|
||
|
||
if (!(assembly is RuntimeAssembly))
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
|
||
|
||
MainAssembly = assembly;
|
||
BaseNameField = baseName;
|
||
|
||
SetAppXConfiguration();
|
||
|
||
CommonAssemblyInit();
|
||
|
||
m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
|
||
// Special case for mscorlib - protect mscorlib's private resources.
|
||
// This isn't for security reasons, but to ensure we can make
|
||
// breaking changes to mscorlib's internal resources without
|
||
// assuming users may have taken a dependency on them.
|
||
if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
|
||
{
|
||
m_callingAssembly = null;
|
||
}
|
||
}
|
||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
|
||
public ResourceManager(String baseName, Assembly assembly, Type usingResourceSet)
|
||
{
|
||
if (null==baseName)
|
||
throw new ArgumentNullException("baseName");
|
||
if (null==assembly)
|
||
throw new ArgumentNullException("assembly");
|
||
Contract.EndContractBlock();
|
||
|
||
#if !FEATURE_CORECLR
|
||
// This constructor is not designed to be used under AppX and is not in the Win8 profile.
|
||
// However designers may use them even if they are running under AppX since they are
|
||
// not subject to the restrictions of the Win8 profile.
|
||
Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
|
||
#endif
|
||
|
||
if (!(assembly is RuntimeAssembly))
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));
|
||
|
||
MainAssembly = assembly;
|
||
BaseNameField = baseName;
|
||
|
||
if (usingResourceSet != null && (usingResourceSet != _minResourceSet) && !(usingResourceSet.IsSubclassOf(_minResourceSet)))
|
||
throw new ArgumentException(Environment.GetResourceString("Arg_ResMgrNotResSet"), "usingResourceSet");
|
||
_userResourceSet = usingResourceSet;
|
||
|
||
CommonAssemblyInit();
|
||
m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
|
||
// Special case for mscorlib - protect mscorlib's private resources.
|
||
// This isn't for security reasons, but to ensure we can make
|
||
// breaking changes to mscorlib's internal resources without
|
||
// assuming users may have taken a dependency on them.
|
||
if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
|
||
m_callingAssembly = null;
|
||
}
|
||
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
|
||
public ResourceManager(Type resourceSource)
|
||
{
|
||
if (null==resourceSource)
|
||
throw new ArgumentNullException("resourceSource");
|
||
Contract.EndContractBlock();
|
||
|
||
if (!(resourceSource is RuntimeType))
|
||
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));
|
||
|
||
_locationInfo = resourceSource;
|
||
MainAssembly = _locationInfo.Assembly;
|
||
BaseNameField = resourceSource.Name;
|
||
|
||
SetAppXConfiguration();
|
||
|
||
CommonAssemblyInit();
|
||
|
||
m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
|
||
// Special case for mscorlib - protect mscorlib's private resources.
|
||
if (MainAssembly == typeof(Object).Assembly && m_callingAssembly != MainAssembly)
|
||
{
|
||
m_callingAssembly = null;
|
||
}
|
||
}
|
||
|
||
[OnDeserializing]
|
||
private void OnDeserializing(StreamingContext ctx)
|
||
{
|
||
this._resourceSets = null;
|
||
this.resourceGroveler = null;
|
||
this._lastUsedResourceCache = null;
|
||
}
|
||
|
||
[System.Security.SecuritySafeCritical]
|
||
[OnDeserialized]
|
||
private void OnDeserialized(StreamingContext ctx)
|
||
{
|
||
_resourceSets = new Dictionary<String, ResourceSet>();
|
||
_lastUsedResourceCache = new CultureNameResourceSetPair();
|
||
// set up resource groveler, depending on whether this ResourceManager
|
||
// is looking for files or assemblies
|
||
ResourceManagerMediator mediator = new ResourceManagerMediator(this);
|
||
if (UseManifest)
|
||
{
|
||
resourceGroveler = new ManifestBasedResourceGroveler(mediator);
|
||
}
|
||
else
|
||
{
|
||
resourceGroveler = new FileBasedResourceGroveler(mediator);
|
||
}
|
||
|
||
// correct callingAssembly for v2
|
||
if (this.m_callingAssembly == null)
|
||
{
|
||
this.m_callingAssembly = (RuntimeAssembly)_callingAssembly;
|
||
}
|
||
|
||
// v2 does this lazily
|
||
if (UseManifest && this._neutralResourcesCulture == null)
|
||
{
|
||
_neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
|
||
}
|
||
}
|
||
|
||
[OnSerializing]
|
||
private void OnSerializing(StreamingContext ctx)
|
||
{
|
||
// Initialize the fields Whidbey expects
|
||
_callingAssembly = m_callingAssembly;
|
||
UseSatelliteAssem = UseManifest;
|
||
#pragma warning disable 618
|
||
ResourceSets = new Hashtable(); // for backward compatibility
|
||
#pragma warning restore 618
|
||
}
|
||
|
||
|
||
// Trying to unify code as much as possible, even though having to do a
|
||
// security check in each constructor prevents it.
|
||
[System.Security.SecuritySafeCritical]
|
||
private void CommonAssemblyInit()
|
||
{
|
||
if (_bUsingModernResourceManagement == false)
|
||
{
|
||
UseManifest = true;
|
||
|
||
_resourceSets = new Dictionary<String,ResourceSet>();
|
||
_lastUsedResourceCache = new CultureNameResourceSetPair();
|
||
|
||
_fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;
|
||
|
||
ResourceManagerMediator mediator = new ResourceManagerMediator(this);
|
||
resourceGroveler = new ManifestBasedResourceGroveler(mediator);
|
||
}
|
||
|
||
_neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
|
||
|
||
#if !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile event providers for coreclr
|
||
if (_bUsingModernResourceManagement == false)
|
||
{
|
||
#if !MONO
|
||
if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
|
||
CultureInfo culture = CultureInfo.InvariantCulture;
|
||
String defaultResName = GetResourceFileName(culture);
|
||
|
||
if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
|
||
FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
|
||
}
|
||
else {
|
||
String outputResName = defaultResName;
|
||
if (_locationInfo != null && _locationInfo.Namespace != null)
|
||
outputResName = _locationInfo.Namespace + Type.Delimiter + defaultResName;
|
||
FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, outputResName);
|
||
}
|
||
}
|
||
#endif
|
||
#pragma warning disable 618
|
||
ResourceSets = new Hashtable(); // for backward compatibility
|
||
#pragma warning restore 618
|
||
}
|
||
#endif
|
||
}
|
||
|
||
// Gets the base name for the ResourceManager.
|
||
public virtual String BaseName {
|
||
get { return BaseNameField; }
|
||
}
|
||
|
||
// Whether we should ignore the capitalization of resources when calling
|
||
// GetString or GetObject.
|
||
public virtual bool IgnoreCase {
|
||
get { return _ignoreCase; }
|
||
set { _ignoreCase = value; }
|
||
}
|
||
|
||
// Returns the Type of the ResourceSet the ResourceManager uses
|
||
// to construct ResourceSets.
|
||
public virtual Type ResourceSetType {
|
||
get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
|
||
}
|
||
|
||
protected UltimateResourceFallbackLocation FallbackLocation
|
||
{
|
||
get { return _fallbackLoc; }
|
||
set { _fallbackLoc = value; }
|
||
}
|
||
|
||
// Tells the ResourceManager to call Close on all ResourceSets and
|
||
// release all resources. This will shrink your working set by
|
||
// potentially a substantial amount in a running application. Any
|
||
// future resource lookups on this ResourceManager will be as
|
||
// expensive as the very first lookup, since it will need to search
|
||
// for files and load resources again.
|
||
//
|
||
// This may be useful in some complex threading scenarios, where
|
||
// creating a new ResourceManager isn't quite the correct behavior.
|
||
public virtual void ReleaseAllResources()
|
||
{
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerReleasingResources(BaseNameField, MainAssembly);
|
||
}
|
||
#endif
|
||
Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
|
||
|
||
// If any calls to Close throw, at least leave ourselves in a
|
||
// consistent state.
|
||
_resourceSets = new Dictionary<String,ResourceSet>();
|
||
_lastUsedResourceCache = new CultureNameResourceSetPair();
|
||
|
||
lock(localResourceSets) {
|
||
IDictionaryEnumerator setEnum = localResourceSets.GetEnumerator();
|
||
|
||
#if !FEATURE_CORECLR
|
||
IDictionaryEnumerator setEnum2 = null;
|
||
#pragma warning disable 618
|
||
if (ResourceSets != null) {
|
||
setEnum2 = ResourceSets.GetEnumerator();
|
||
}
|
||
ResourceSets = new Hashtable(); // for backwards compat
|
||
#pragma warning restore 618
|
||
#endif
|
||
|
||
while (setEnum.MoveNext()) {
|
||
((ResourceSet)setEnum.Value).Close();
|
||
}
|
||
|
||
#if !FEATURE_CORECLR
|
||
if (setEnum2 != null) {
|
||
while (setEnum2.MoveNext()) {
|
||
((ResourceSet)setEnum2.Value).Close();
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
[ResourceExposure(ResourceScope.Machine)]
|
||
[ResourceConsumption(ResourceScope.Machine)]
|
||
public static ResourceManager CreateFileBasedResourceManager(String baseName, String resourceDir, Type usingResourceSet)
|
||
{
|
||
return new ResourceManager(baseName, resourceDir, usingResourceSet);
|
||
}
|
||
|
||
// Given a CultureInfo, GetResourceFileName generates the name for
|
||
// the binary file for the given CultureInfo. This method uses
|
||
// CultureInfo's Name property as part of the file name for all cultures
|
||
// other than the invariant culture. This method does not touch the disk,
|
||
// and is used only to construct what a resource file name (suitable for
|
||
// passing to the ResourceReader constructor) or a manifest resource file
|
||
// name should look like.
|
||
//
|
||
// This method can be overriden to look for a different extension,
|
||
// such as ".ResX", or a completely different format for naming files.
|
||
protected virtual String GetResourceFileName(CultureInfo culture) {
|
||
StringBuilder sb = new StringBuilder(255);
|
||
sb.Append(BaseNameField);
|
||
// If this is the neutral culture, don't append culture name.
|
||
if (!culture.HasInvariantCultureName)
|
||
{
|
||
CultureInfo.VerifyCultureName(culture.Name, true);
|
||
sb.Append('.');
|
||
sb.Append(culture.Name);
|
||
}
|
||
sb.Append(ResFileExtension);
|
||
return sb.ToString();
|
||
}
|
||
|
||
// WARNING: This function must be kept in [....] with ResourceFallbackManager.GetEnumerator()
|
||
// Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
|
||
internal ResourceSet GetFirstResourceSet(CultureInfo culture)
|
||
{
|
||
// Logic from ResourceFallbackManager.GetEnumerator()
|
||
if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name)
|
||
{
|
||
culture = CultureInfo.InvariantCulture;
|
||
}
|
||
|
||
if(_lastUsedResourceCache != null) {
|
||
lock (_lastUsedResourceCache) {
|
||
if (culture.Name == _lastUsedResourceCache.lastCultureName)
|
||
return _lastUsedResourceCache.lastResourceSet;
|
||
}
|
||
}
|
||
|
||
// Look in the ResourceSet table
|
||
Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
|
||
ResourceSet rs = null;
|
||
if (localResourceSets != null)
|
||
{
|
||
lock (localResourceSets)
|
||
{
|
||
localResourceSets.TryGetValue(culture.Name, out rs);
|
||
}
|
||
}
|
||
|
||
if (rs != null)
|
||
{
|
||
// update the cache with the most recent ResourceSet
|
||
if (_lastUsedResourceCache != null) {
|
||
lock (_lastUsedResourceCache) {
|
||
_lastUsedResourceCache.lastCultureName = culture.Name;
|
||
_lastUsedResourceCache.lastResourceSet = rs;
|
||
}
|
||
}
|
||
return rs;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
// Looks up a set of resources for a particular CultureInfo. This is
|
||
// not useful for most users of the ResourceManager - call
|
||
// GetString() or GetObject() instead.
|
||
//
|
||
// The parameters let you control whether the ResourceSet is created
|
||
// if it hasn't yet been loaded and if parent CultureInfos should be
|
||
// loaded as well for resource inheritance.
|
||
//
|
||
[System.Security.SecuritySafeCritical] // auto-generated
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
|
||
public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) {
|
||
if (null==culture)
|
||
throw new ArgumentNullException("culture");
|
||
Contract.EndContractBlock();
|
||
|
||
Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
|
||
ResourceSet rs;
|
||
if (localResourceSets != null) {
|
||
lock (localResourceSets) {
|
||
if (localResourceSets.TryGetValue(culture.Name, out rs))
|
||
return rs;
|
||
}
|
||
}
|
||
|
||
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
|
||
|
||
if (UseManifest && culture.HasInvariantCultureName) {
|
||
string fileName = GetResourceFileName(culture);
|
||
RuntimeAssembly mainAssembly = (RuntimeAssembly)MainAssembly;
|
||
Stream stream = mainAssembly.GetManifestResourceStream(_locationInfo, fileName, m_callingAssembly == MainAssembly, ref stackMark);
|
||
if (createIfNotExists && stream!=null) {
|
||
rs = ((ManifestBasedResourceGroveler)resourceGroveler).CreateResourceSet(stream, MainAssembly);
|
||
AddResourceSet(localResourceSets, culture.Name, ref rs);
|
||
return rs;
|
||
}
|
||
}
|
||
|
||
// Note: ideally we could plumb through the stack crawl mark here, but we must
|
||
// call the virtual 3-argument InternalGetResourceSet method for compatibility.
|
||
// Security-wise, we're not overly interested in protecting access to resources,
|
||
// since full-trust callers can get them already and most resources are public.
|
||
// Also, the JIT inliner could always inline a caller into another assembly's
|
||
// method, so the entire idea of a security check written this way is ----.
|
||
// So if we happen to return some resources in cases where we should really be
|
||
// doing a demand for member access permissions, we're not overly concerned.
|
||
// <
|
||
return InternalGetResourceSet(culture, createIfNotExists, tryParents);
|
||
}
|
||
|
||
// InternalGetResourceSet is a non-threadsafe method where all the logic
|
||
// for getting a resource set lives. Access to it is controlled by
|
||
// threadsafe methods such as GetResourceSet, GetString, & GetObject.
|
||
// This will take a minimal number of locks.
|
||
[System.Security.SecuritySafeCritical] // auto-generated
|
||
[ResourceExposure(ResourceScope.None)]
|
||
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
|
||
protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
|
||
{
|
||
Contract.Assert(culture != null, "culture != null");
|
||
|
||
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
|
||
return InternalGetResourceSet(culture,createIfNotExists,tryParents, ref stackMark);
|
||
}
|
||
|
||
// InternalGetResourceSet is a non-threadsafe method where all the logic
|
||
// for getting a resource set lives. Access to it is controlled by
|
||
// threadsafe methods such as GetResourceSet, GetString, & GetObject.
|
||
// This will take a minimal number of locks.
|
||
[System.Security.SecurityCritical]
|
||
[ResourceExposure(ResourceScope.None)]
|
||
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||
private ResourceSet InternalGetResourceSet(CultureInfo requestedCulture, bool createIfNotExists, bool tryParents, ref StackCrawlMark stackMark)
|
||
{
|
||
Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
|
||
ResourceSet rs = null;
|
||
CultureInfo foundCulture = null;
|
||
lock (localResourceSets) {
|
||
if (localResourceSets.TryGetValue(requestedCulture.Name, out rs)) {
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized) {
|
||
FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, requestedCulture.Name);
|
||
}
|
||
#endif
|
||
return rs;
|
||
}
|
||
}
|
||
|
||
ResourceFallbackManager mgr = new ResourceFallbackManager(requestedCulture, _neutralResourcesCulture, tryParents);
|
||
|
||
foreach (CultureInfo currentCultureInfo in mgr)
|
||
{
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerLookingForResourceSet(BaseNameField, MainAssembly, currentCultureInfo.Name);
|
||
}
|
||
#endif
|
||
lock(localResourceSets) {
|
||
if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs)) {
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, currentCultureInfo.Name);
|
||
}
|
||
#endif
|
||
// we need to update the cache if we fellback
|
||
if(requestedCulture != currentCultureInfo) foundCulture = currentCultureInfo;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// InternalGetResourceSet will never be threadsafe. However, it must
|
||
// be protected against reentrancy from the SAME THREAD. (ie, calling
|
||
// GetSatelliteAssembly may send some window messages or trigger the
|
||
// Assembly load event, which could fail then call back into the
|
||
// ResourceManager). It's happened.
|
||
|
||
rs = resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets,
|
||
tryParents, createIfNotExists, ref stackMark);
|
||
|
||
// found a ResourceSet; we're done
|
||
if (rs != null)
|
||
{
|
||
foundCulture = currentCultureInfo;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (rs != null && foundCulture != null)
|
||
{
|
||
// add entries to the cache for the cultures we have gone through
|
||
|
||
// currentCultureInfo now refers to the culture that had resources.
|
||
// update cultures starting from requested culture up to the culture
|
||
// that had resources.
|
||
foreach (CultureInfo updateCultureInfo in mgr)
|
||
{
|
||
AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);
|
||
|
||
// stop when we've added current or reached invariant (top of chain)
|
||
if (updateCultureInfo == foundCulture)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return rs;
|
||
}
|
||
|
||
// Simple helper to ease maintenance and improve readability.
|
||
private static void AddResourceSet(Dictionary<String,ResourceSet> localResourceSets, String cultureName, ref ResourceSet rs)
|
||
{
|
||
// InternalGetResourceSet is both recursive and reentrant -
|
||
// assembly load callbacks in particular are a way we can call
|
||
// back into the ResourceManager in unexpectedly on the same thread.
|
||
lock(localResourceSets) {
|
||
// If another thread added this culture, return that.
|
||
ResourceSet lostRace;
|
||
if (localResourceSets.TryGetValue(cultureName, out lostRace)) {
|
||
if (!Object.ReferenceEquals(lostRace, rs)) {
|
||
// Note: In certain cases, we can be trying to add a ResourceSet for multiple
|
||
// cultures on one thread, while a second thread added another ResourceSet for one
|
||
// of those cultures. So when we lose the ----, we must make sure our ResourceSet
|
||
// isn't in our dictionary before closing it.
|
||
if (!localResourceSets.ContainsValue(rs))
|
||
rs.Dispose();
|
||
rs = lostRace;
|
||
}
|
||
}
|
||
else {
|
||
localResourceSets.Add(cultureName, rs);
|
||
}
|
||
}
|
||
}
|
||
|
||
protected static Version GetSatelliteContractVersion(Assembly a)
|
||
{
|
||
// Ensure that the assembly reference is not null
|
||
if (a == null) {
|
||
throw new ArgumentNullException("a", Environment.GetResourceString("ArgumentNull_Assembly"));
|
||
}
|
||
Contract.EndContractBlock();
|
||
|
||
#if !FEATURE_WINDOWSPHONE
|
||
String v = null;
|
||
if (a.ReflectionOnly) {
|
||
foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(a)) {
|
||
if (data.Constructor.DeclaringType == typeof(SatelliteContractVersionAttribute)) {
|
||
v = (String)data.ConstructorArguments[0].Value;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (v == null)
|
||
return null;
|
||
}
|
||
else {
|
||
Object[] attrs = a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
|
||
if (attrs.Length == 0)
|
||
return null;
|
||
Contract.Assert(attrs.Length == 1, "Cannot have multiple instances of SatelliteContractVersionAttribute on an assembly!");
|
||
v = ((SatelliteContractVersionAttribute)attrs[0]).Version;
|
||
}
|
||
Version ver;
|
||
try {
|
||
ver = new Version(v);
|
||
}
|
||
catch(ArgumentOutOfRangeException e) {
|
||
// Note we are prone to hitting infinite loops if mscorlib's
|
||
// SatelliteContractVersionAttribute contains bogus values.
|
||
// If this assert fires, please fix the build process for the
|
||
// BCL directory.
|
||
if (a == typeof(Object).Assembly) {
|
||
Contract.Assert(false, "mscorlib's SatelliteContractVersionAttribute is a malformed version string!");
|
||
return null;
|
||
}
|
||
|
||
throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSatelliteContract_Asm_Ver", a.ToString(), v), e);
|
||
}
|
||
return ver;
|
||
#else
|
||
// On the phone return null. The calling code will use the assembly version instead to avoid potential type
|
||
// and library loads caused by CA lookup. NetCF uses the assembly version always.
|
||
return null;
|
||
#endif
|
||
}
|
||
|
||
[System.Security.SecuritySafeCritical] // auto-generated
|
||
protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
|
||
{
|
||
// This method should be obsolete - replace it with the one below.
|
||
// Unfortunately, we made it protected.
|
||
UltimateResourceFallbackLocation ignoringUsefulData = UltimateResourceFallbackLocation.MainAssembly;
|
||
CultureInfo culture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, ref ignoringUsefulData);
|
||
return culture;
|
||
}
|
||
|
||
// IGNORES VERSION
|
||
internal static bool CompareNames(String asmTypeName1,
|
||
String typeName2,
|
||
AssemblyName asmName2)
|
||
{
|
||
Contract.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null");
|
||
|
||
// First, compare type names
|
||
int comma = asmTypeName1.IndexOf(',');
|
||
if (((comma == -1) ? asmTypeName1.Length : comma) != typeName2.Length)
|
||
return false;
|
||
|
||
// case sensitive
|
||
if (String.Compare(asmTypeName1, 0, typeName2, 0, typeName2.Length, StringComparison.Ordinal) != 0)
|
||
return false;
|
||
if (comma == -1)
|
||
return true;
|
||
|
||
// Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
|
||
// also, for mscorlib ignores everything, since that's what the binder is going to do
|
||
while(Char.IsWhiteSpace(asmTypeName1[++comma]));
|
||
|
||
// case insensitive
|
||
AssemblyName an1 = new AssemblyName(asmTypeName1.Substring(comma));
|
||
if (String.Compare(an1.Name, asmName2.Name, StringComparison.OrdinalIgnoreCase) != 0)
|
||
return false;
|
||
|
||
// to match IsMscorlib() in VM
|
||
if (String.Compare(an1.Name, "mscorlib", StringComparison.OrdinalIgnoreCase) == 0)
|
||
return true;
|
||
|
||
|
||
if ((an1.CultureInfo != null) && (asmName2.CultureInfo != null) &&
|
||
#if FEATURE_USE_LCID
|
||
(an1.CultureInfo.LCID != asmName2.CultureInfo.LCID)
|
||
#else
|
||
(an1.CultureInfo.Name != asmName2.CultureInfo.Name)
|
||
#endif
|
||
)
|
||
return false;
|
||
|
||
byte[] pkt1 = an1.GetPublicKeyToken();
|
||
byte[] pkt2 = asmName2.GetPublicKeyToken();
|
||
if ((pkt1 != null) && (pkt2 != null)) {
|
||
if (pkt1.Length != pkt2.Length)
|
||
return false;
|
||
|
||
for(int i=0; i < pkt1.Length; i++) {
|
||
if(pkt1[i] != pkt2[i])
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
#if FEATURE_APPX
|
||
[SecuritySafeCritical]
|
||
// Throws WinRT hresults
|
||
private string GetStringFromPRI(String stringName, String startingCulture, String neutralResourcesCulture) {
|
||
Contract.Assert(_bUsingModernResourceManagement);
|
||
Contract.Assert(_WinRTResourceManager != null);
|
||
Contract.Assert(_PRIonAppXInitialized);
|
||
Contract.Assert(AppDomain.IsAppXModel());
|
||
|
||
if (stringName.Length == 0)
|
||
return null;
|
||
|
||
string resourceString = null;
|
||
|
||
// Do not handle exceptions. See the comment in SetAppXConfiguration about throwing
|
||
// exception types that the ResourceManager class is not documented to throw.
|
||
resourceString = _WinRTResourceManager.GetString(
|
||
stringName,
|
||
String.IsNullOrEmpty(startingCulture) ? null : startingCulture,
|
||
String.IsNullOrEmpty(neutralResourcesCulture) ? null : neutralResourcesCulture);
|
||
|
||
return resourceString;
|
||
}
|
||
|
||
// Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the type via reflection.
|
||
// It would be better if we could just implement WindowsRuntimeResourceManager in mscorlib, but we can't, because
|
||
// we can do very little with WinRT in mscorlib.
|
||
[SecurityCritical]
|
||
internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
|
||
{
|
||
Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
|
||
return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
|
||
}
|
||
#endif
|
||
|
||
[NonSerialized]
|
||
private bool _bUsingModernResourceManagement; // Written only by SetAppXConfiguration
|
||
|
||
#if FEATURE_APPX
|
||
[NonSerialized]
|
||
[SecurityCritical]
|
||
private WindowsRuntimeResourceManagerBase _WinRTResourceManager; // Written only by SetAppXConfiguration
|
||
|
||
[NonSerialized]
|
||
private bool _PRIonAppXInitialized; // Written only by SetAppXConfiguration
|
||
|
||
[NonSerialized]
|
||
private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration
|
||
|
||
// When running under AppX, the following rules apply for resource lookup:
|
||
//
|
||
// Desktop
|
||
// -------
|
||
//
|
||
// 1) For Framework assemblies, we always use satellite assembly based lookup.
|
||
// 2) For non-FX assemblies, we use modern resource manager, with the premise being that app package
|
||
// contains the PRI resources since such assemblies are expected to be application assemblies.
|
||
//
|
||
// CoreCLR
|
||
// -------
|
||
//
|
||
// 1) For Framework assemblies, we always use satellite assembly based lookup.
|
||
// 2) For non-FX assemblies:
|
||
//
|
||
// a) If the assembly lives under PLATFORM_RESOURCE_ROOTS (as specified by the host during AppDomain creation),
|
||
// then we will use satellite assembly based lookup in assemblies like *.resources.dll.
|
||
//
|
||
// b) For any other non-FX assembly, we will use the modern resource manager with the premise that app package
|
||
// contains the PRI resources.
|
||
[SecuritySafeCritical]
|
||
private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(RuntimeAssembly resourcesAssembly)
|
||
{
|
||
bool fUseSatelliteAssemblyResourceLookupUnderAppX = resourcesAssembly.IsFrameworkAssembly();
|
||
|
||
#if FEATURE_CORECLR
|
||
if (!fUseSatelliteAssemblyResourceLookupUnderAppX)
|
||
{
|
||
// Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
|
||
String platformResourceRoots = (String)(AppDomain.CurrentDomain.GetData("PLATFORM_RESOURCE_ROOTS"));
|
||
if ((platformResourceRoots != null) && (platformResourceRoots != String.Empty))
|
||
{
|
||
string resourceAssemblyPath = resourcesAssembly.Location;
|
||
|
||
// Loop through the PLATFORM_RESOURCE_ROOTS and see if the assembly is contained in it.
|
||
foreach(string pathPlatformResourceRoot in platformResourceRoots.Split(Path.PathSeparator))
|
||
{
|
||
if (resourceAssemblyPath.StartsWith(pathPlatformResourceRoot, StringComparison.CurrentCultureIgnoreCase))
|
||
{
|
||
// Found the resource assembly to be present in one of the PLATFORM_RESOURCE_ROOT, so stop the enumeration loop.
|
||
fUseSatelliteAssemblyResourceLookupUnderAppX = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif // FEATURE_CORECLR
|
||
return fUseSatelliteAssemblyResourceLookupUnderAppX;
|
||
|
||
}
|
||
|
||
[SecuritySafeCritical]
|
||
#endif // FEATURE_APPX
|
||
// Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
|
||
// Throws MissingManifestResourceException and WinRT HResults
|
||
|
||
private void SetAppXConfiguration()
|
||
{
|
||
Contract.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member
|
||
#if FEATURE_APPX
|
||
Contract.Assert(_WinRTResourceManager == null); // Only this function writes to this member
|
||
Contract.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
|
||
Contract.Assert(_PRIExceptionInfo == null); // Only this function writes to this member
|
||
|
||
bool bUsingSatelliteAssembliesUnderAppX = false;
|
||
|
||
RuntimeAssembly resourcesAssembly = (RuntimeAssembly)MainAssembly;
|
||
|
||
if (resourcesAssembly == null)
|
||
resourcesAssembly = m_callingAssembly;
|
||
|
||
if (resourcesAssembly != null)
|
||
{
|
||
if (resourcesAssembly != typeof(Object).Assembly) // We are not loading resources for mscorlib
|
||
{
|
||
// Cannot load the WindowsRuntimeResourceManager when in a compilation process, since it
|
||
// lives in System.Runtime.WindowsRuntime and only mscorlib may be loaded for execution.
|
||
if (AppDomain.IsAppXModel() && !AppDomain.IsAppXNGen)
|
||
{
|
||
s_IsAppXModel = true;
|
||
|
||
// If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField.
|
||
String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName;
|
||
|
||
// The only way this can happen is if a class inherited from ResourceManager and
|
||
// did not set the BaseNameField before calling the protected ResourceManager() constructor.
|
||
// For other constructors, we would already have thrown an ArgumentNullException by now.
|
||
// Throwing an ArgumentNullException now is not the right thing to do because technically
|
||
// ResourceManager() takes no arguments, and because it is not documented as throwing
|
||
// any exceptions. Instead, let's go through the rest of the initialization with this set to
|
||
// an empty string. We may in fact fail earlier for another reason, but otherwise we will
|
||
// throw a MissingManifestResourceException when GetString is called indicating that a
|
||
// resW filename called "" could not be found.
|
||
if (reswFilename == null)
|
||
reswFilename = String.Empty;
|
||
|
||
WindowsRuntimeResourceManagerBase WRRM = null;
|
||
bool bWRRM_Initialized = false;
|
||
|
||
if (AppDomain.IsAppXDesignMode())
|
||
{
|
||
WRRM = GetWinRTResourceManager();
|
||
try {
|
||
PRIExceptionInfo exceptionInfo; // If the exception info is filled in, we will ignore it.
|
||
bWRRM_Initialized = WRRM.Initialize(resourcesAssembly.Location, reswFilename, out exceptionInfo);
|
||
bUsingSatelliteAssembliesUnderAppX = !bWRRM_Initialized;
|
||
}
|
||
catch(Exception e)
|
||
{
|
||
bUsingSatelliteAssembliesUnderAppX = true;
|
||
if (e.IsTransient)
|
||
throw;
|
||
}
|
||
}
|
||
|
||
if (!bUsingSatelliteAssembliesUnderAppX)
|
||
{
|
||
// See AssemblyNative::IsFrameworkAssembly for details on which kinds of assemblies are considered Framework assemblies.
|
||
// The Modern Resource Manager is not used for such assemblies - they continue to use satellite assemblies (i.e. .resources.dll files).
|
||
_bUsingModernResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(resourcesAssembly);
|
||
|
||
if (_bUsingModernResourceManagement)
|
||
{
|
||
// Only now are we certain that we need the PRI file.
|
||
|
||
// Note that if IsAppXDesignMode is false, we haven't checked if the PRI file exists.
|
||
// This is by design. We will find out in the call to WindowsRuntimeResourceManager.Initialize below.
|
||
|
||
// At this point it is important NOT to set _bUsingModernResourceManagement to false
|
||
// if the PRI file does not exist because we are now certain we need to load PRI
|
||
// resources. We want to fail by throwing a MissingManifestResourceException
|
||
// if WindowsRuntimeResourceManager.Initialize fails to locate the PRI file. We do not
|
||
// want to fall back to using satellite assemblies anymore. Note that we would not throw
|
||
// the MissingManifestResourceException from this function, but from GetString. See the
|
||
// comment below on the reason for this.
|
||
|
||
if (WRRM != null && bWRRM_Initialized)
|
||
{
|
||
// Reuse the one successfully created earlier
|
||
_WinRTResourceManager = WRRM;
|
||
_PRIonAppXInitialized = true;
|
||
}
|
||
else
|
||
{
|
||
_WinRTResourceManager = GetWinRTResourceManager();
|
||
|
||
try {
|
||
_PRIonAppXInitialized = _WinRTResourceManager.Initialize(resourcesAssembly.Location, reswFilename, out _PRIExceptionInfo);
|
||
|
||
// Note that _PRIExceptionInfo might be null - this is OK.
|
||
// In that case we will just throw the generic
|
||
// MissingManifestResource_NoPRIresources exception.
|
||
// See the implementation of GetString for more details.
|
||
}
|
||
// We would like to be able to throw a MissingManifestResourceException here if PRI resources
|
||
// could not be loaded for a recognized reason. However, the ResourceManager constructors
|
||
// that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException,
|
||
// and since they are part of the portable profile, we cannot start throwing a new exception type
|
||
// as that would break existing portable libraries. Hence we must save the exception information
|
||
// now and throw the exception on the first call to GetString.
|
||
catch(FileNotFoundException)
|
||
{
|
||
// We will throw MissingManifestResource_NoPRIresources from GetString
|
||
// when we see that _PRIonAppXInitialized is false.
|
||
}
|
||
catch(Exception e)
|
||
{
|
||
// ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps
|
||
// in WindowsRuntimeResourceManager.Initialize.
|
||
// In this case _PRIExceptionInfo is now null and we will just throw the generic
|
||
// MissingManifestResource_NoPRIresources exception.
|
||
// See the implementation of GetString for more details.
|
||
if (e.HResult != __HResults.ERROR_MRM_MAP_NOT_FOUND)
|
||
throw; // Unexpected exception code. Bubble it up to the caller.
|
||
}
|
||
// Allow all other exception types to bubble up to the caller.
|
||
|
||
// Yes, this causes us to potentially throw exception types that are not documented.
|
||
|
||
// Ultimately the tradeoff is the following:
|
||
// -We could ignore unknown exceptions or rethrow them as inner exceptions
|
||
// of exceptions that the ResourceManager class is already documented as throwing.
|
||
// This would allow existing portable libraries to gracefully recover if they don't care
|
||
// too much about the ResourceManager object they are using. However it could
|
||
// mask potentially fatal errors that we are not aware of, such as a disk drive failing.
|
||
|
||
|
||
// The alternative, which we chose, is to throw unknown exceptions. This may tear
|
||
// down the process if the portable library and app don't expect this exception type.
|
||
// On the other hand, this won't mask potentially fatal errors we don't know about.
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// resourcesAssembly == null should not happen but it can. See the comment on Assembly.GetCallingAssembly.
|
||
// However for the sake of 100% backwards compatibility on Win7 and below, we must leave
|
||
// _bUsingModernResourceManagement as false.
|
||
#endif // FEATURE_APPX
|
||
}
|
||
|
||
// Looks up a resource value for a particular name. Looks in the
|
||
// current thread's CultureInfo, and if not found, all parent CultureInfos.
|
||
// Returns null if the resource wasn't found.
|
||
//
|
||
public virtual String GetString(String name) {
|
||
return GetString(name, (CultureInfo)null);
|
||
}
|
||
|
||
// Looks up a resource value for a particular name. Looks in the
|
||
// specified CultureInfo, and if not found, all parent CultureInfos.
|
||
// Returns null if the resource wasn't found.
|
||
//
|
||
public virtual String GetString(String name, CultureInfo culture) {
|
||
if (null==name)
|
||
throw new ArgumentNullException("name");
|
||
Contract.EndContractBlock();
|
||
|
||
#if FEATURE_APPX
|
||
if(s_IsAppXModel)
|
||
{
|
||
// If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
|
||
// null it out, so that we re-compute it. If we use modern resource lookup, we may end up getting a "better"
|
||
// match, since CultureInfo objects can't represent all the different languages the AppX resource model supports.
|
||
// For classic resources, this causes us to ignore the languages list and instead use the older Win32 behavior,
|
||
// which is the design choice we've made. (See the call a little later to GetCurrentUICultureNoAppX()).
|
||
if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
|
||
{
|
||
culture = null;
|
||
}
|
||
}
|
||
|
||
if (_bUsingModernResourceManagement)
|
||
{
|
||
if (_PRIonAppXInitialized == false)
|
||
{
|
||
// Always throw if we did not fully succeed in initializing the WinRT Resource Manager.
|
||
|
||
if (_PRIExceptionInfo != null && _PRIExceptionInfo._PackageSimpleName != null && _PRIExceptionInfo._ResWFile != null)
|
||
throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_ResWFileNotLoaded", _PRIExceptionInfo._ResWFile, _PRIExceptionInfo._PackageSimpleName));
|
||
|
||
throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoPRIresources"));
|
||
}
|
||
|
||
// Throws WinRT hresults.
|
||
return GetStringFromPRI(name,
|
||
culture == null ? null : culture.Name,
|
||
_neutralResourcesCulture.Name);
|
||
}
|
||
else
|
||
#endif // FEATURE_APPX
|
||
{
|
||
if (null==culture) {
|
||
// When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
|
||
// This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
|
||
culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
|
||
}
|
||
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
|
||
}
|
||
#endif
|
||
ResourceSet last = GetFirstResourceSet(culture);
|
||
|
||
if (last != null)
|
||
{
|
||
String value = last.GetString(name, _ignoreCase);
|
||
if (value != null)
|
||
return value;
|
||
}
|
||
|
||
|
||
// This is the CultureInfo hierarchy traversal code for resource
|
||
// lookups, similar but necessarily orthogonal to the ResourceSet
|
||
// lookup logic.
|
||
ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
|
||
foreach (CultureInfo currentCultureInfo in mgr) {
|
||
|
||
ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
|
||
if (rs == null)
|
||
break;
|
||
|
||
if (rs != last) {
|
||
String value = rs.GetString(name, _ignoreCase);
|
||
if (value != null)
|
||
{
|
||
// update last used ResourceSet
|
||
if (_lastUsedResourceCache != null) {
|
||
lock (_lastUsedResourceCache) {
|
||
_lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
|
||
_lastUsedResourceCache.lastResourceSet = rs;
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
|
||
last = rs;
|
||
}
|
||
}
|
||
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
|
||
// Looks up a resource value for a particular name. Looks in the
|
||
// current thread's CultureInfo, and if not found, all parent CultureInfos.
|
||
// Returns null if the resource wasn't found.
|
||
//
|
||
public virtual Object GetObject(String name) {
|
||
return GetObject(name, (CultureInfo)null, true);
|
||
}
|
||
|
||
// Looks up a resource value for a particular name. Looks in the
|
||
// specified CultureInfo, and if not found, all parent CultureInfos.
|
||
// Returns null if the resource wasn't found.
|
||
public virtual Object GetObject(String name, CultureInfo culture) {
|
||
return GetObject(name, culture, true);
|
||
}
|
||
|
||
private Object GetObject(String name, CultureInfo culture, bool wrapUnmanagedMemStream)
|
||
{
|
||
if (null==name)
|
||
throw new ArgumentNullException("name");
|
||
Contract.EndContractBlock();
|
||
|
||
#if FEATURE_APPX
|
||
if(s_IsAppXModel)
|
||
{
|
||
// If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
|
||
// null it out, so that we re-compute it based on the Win32 value and not the AppX language list value.
|
||
// (See the call a little later to GetCurrentUICultureNoAppX()).
|
||
if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
|
||
{
|
||
culture = null;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (null==culture) {
|
||
// When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
|
||
// This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
|
||
culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
|
||
}
|
||
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
|
||
}
|
||
#endif
|
||
ResourceSet last = GetFirstResourceSet(culture);
|
||
if (last != null)
|
||
{
|
||
Object value = last.GetObject(name, _ignoreCase);
|
||
|
||
if (value != null)
|
||
{
|
||
UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
|
||
if (stream != null && wrapUnmanagedMemStream)
|
||
return new UnmanagedMemoryStreamWrapper(stream);
|
||
else
|
||
return value;
|
||
}
|
||
}
|
||
|
||
// This is the CultureInfo hierarchy traversal code for resource
|
||
// lookups, similar but necessarily orthogonal to the ResourceSet
|
||
// lookup logic.
|
||
ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
|
||
|
||
foreach (CultureInfo currentCultureInfo in mgr) {
|
||
// Note: Technically this method should be passed in a stack crawl mark that we then pass
|
||
// to InternalGetResourceSet for ensuring we demand permissions to read your private resources
|
||
// if you're reading resources from an assembly other than yourself. But, we must call our
|
||
// three argument overload (without the stack crawl mark) for compatibility. After
|
||
// consideration, we aren't worried about the security impact.
|
||
ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
|
||
if (rs == null)
|
||
break;
|
||
|
||
if (rs != last) {
|
||
Object value = rs.GetObject(name, _ignoreCase);
|
||
if (value != null) {
|
||
// update the last used ResourceSet
|
||
if (_lastUsedResourceCache != null) {
|
||
lock (_lastUsedResourceCache) {
|
||
_lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
|
||
_lastUsedResourceCache.lastResourceSet = rs;
|
||
}
|
||
}
|
||
|
||
UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
|
||
if (stream != null && wrapUnmanagedMemStream)
|
||
return new UnmanagedMemoryStreamWrapper(stream);
|
||
else
|
||
return value;
|
||
}
|
||
|
||
last = rs;
|
||
}
|
||
}
|
||
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
|
||
}
|
||
#endif
|
||
return null;
|
||
}
|
||
|
||
[ComVisible(false)]
|
||
public UnmanagedMemoryStream GetStream(String name) {
|
||
return GetStream(name, (CultureInfo)null);
|
||
}
|
||
|
||
[ComVisible(false)]
|
||
public UnmanagedMemoryStream GetStream(String name, CultureInfo culture) {
|
||
Object obj = GetObject(name, culture, false);
|
||
UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
|
||
if (ums == null && obj != null)
|
||
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotStream_Name", name));
|
||
return ums;
|
||
}
|
||
|
||
#if RESOURCE_SATELLITE_CONFIG
|
||
// Internal helper method - gives an end user the ability to prevent
|
||
// satellite assembly probes for certain cultures via a config file.
|
||
[System.Security.SecurityCritical] // auto-generated
|
||
private bool TryLookingForSatellite(CultureInfo lookForCulture)
|
||
{
|
||
if (!_checkedConfigFile) {
|
||
lock (this) {
|
||
if (!_checkedConfigFile) {
|
||
_checkedConfigFile = true;
|
||
_installedSatelliteInfo = GetSatelliteAssembliesFromConfig();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (_installedSatelliteInfo == null)
|
||
return true;
|
||
|
||
String[] installedSatellites = (String[]) _installedSatelliteInfo[MainAssembly.FullName];
|
||
|
||
if (installedSatellites == null)
|
||
return true;
|
||
|
||
// The config file told us what satellites might be installed.
|
||
int pos = Array.IndexOf(installedSatellites, lookForCulture.Name);
|
||
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
|
||
if (pos < 0) {
|
||
FrameworkEventSource.Log.ResourceManagerCultureNotFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
|
||
}
|
||
else {
|
||
FrameworkEventSource.Log.ResourceManagerCultureFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
|
||
}
|
||
}
|
||
#endif
|
||
return pos >= 0;
|
||
}
|
||
|
||
// Note: There is one config file per appdomain. This is not
|
||
// per-process nor per-assembly.
|
||
[System.Security.SecurityCritical] // auto-generated
|
||
[ResourceExposure(ResourceScope.None)]
|
||
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||
private Hashtable GetSatelliteAssembliesFromConfig()
|
||
{
|
||
#if FEATURE_FUSION
|
||
|
||
String fileName = AppDomain.CurrentDomain.FusionStore.ConfigurationFileInternal;
|
||
if (fileName == null) {
|
||
return null;
|
||
}
|
||
|
||
// Don't do a security assert. We need to support semi-trusted
|
||
// scenarios, but asserting here causes infinite resource lookups
|
||
// while initializing security & looking up mscorlib's config file.
|
||
// Use internal methods to bypass security checks.
|
||
|
||
// If we're dealing with a local file name or a UNC path instead
|
||
// of a URL, check to see if the file exists here for perf (avoids
|
||
// throwing a FileNotFoundException).
|
||
if (fileName.Length >= 2 &&
|
||
((fileName[1] == Path.VolumeSeparatorChar) || (fileName[0] == Path.DirectorySeparatorChar && fileName[1] == Path.DirectorySeparatorChar)) &&
|
||
!File.InternalExists(fileName))
|
||
return null;
|
||
|
||
ConfigTreeParser parser = new ConfigTreeParser();
|
||
String queryPath = "/configuration/satelliteassemblies";
|
||
ConfigNode node = null;
|
||
// Catch exceptions in case a web app doesn't have a config file.
|
||
try {
|
||
node = parser.Parse(fileName, queryPath, true);
|
||
}
|
||
catch(Exception) {}
|
||
|
||
if (node == null) {
|
||
return null;
|
||
}
|
||
|
||
// The application config file will contain sections like this:
|
||
// <?xml version="1.0"?>
|
||
// <configuration>
|
||
// <satelliteassemblies>
|
||
// <assembly name="mscorlib, Version=..., PublicKeyToken=...">
|
||
// <culture>fr</culture>
|
||
// </assembly>
|
||
// <assembly name="UserAssembly, ...">
|
||
// <culture>fr-FR</culture>
|
||
// <culture>de-CH</culture>
|
||
// </assembly>
|
||
// <assembly name="UserAssembly2, ...">
|
||
// </assembly>
|
||
// </satelliteassemblies>
|
||
// </configuration>
|
||
Hashtable satelliteInfo = new Hashtable(StringComparer.OrdinalIgnoreCase);
|
||
foreach(ConfigNode assemblyNode in node.Children) {
|
||
if (!String.Equals(assemblyNode.Name, "assembly"))
|
||
throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTag", Path.GetFileName(fileName), assemblyNode.Name));
|
||
|
||
if (assemblyNode.Attributes.Count == 0)
|
||
throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagNoAttr", Path.GetFileName(fileName)));
|
||
|
||
DictionaryEntry de = (DictionaryEntry) assemblyNode.Attributes[0];
|
||
String assemblyName = (String) de.Value;
|
||
if (!String.Equals(de.Key, "name") || String.IsNullOrEmpty(assemblyName) || assemblyNode.Attributes.Count > 1)
|
||
throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagBadAttr", Path.GetFileName(fileName), de.Key, de.Value));
|
||
|
||
ArrayList list = new ArrayList(5);
|
||
foreach(ConfigNode child in assemblyNode.Children)
|
||
if (child.Value != null)
|
||
list.Add(child.Value);
|
||
|
||
String[] satellites = new String[list.Count];
|
||
for(int i=0; i<satellites.Length; i++) {
|
||
String cultureName = (String)list[i];
|
||
satellites[i] = cultureName;
|
||
#if !FEATURE_CORECLR && !MONO
|
||
if (FrameworkEventSource.IsInitialized)
|
||
{
|
||
FrameworkEventSource.Log.ResourceManagerAddingCultureFromConfigFile(BaseNameField, MainAssembly, cultureName);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
satelliteInfo.Add(assemblyName, satellites);
|
||
}
|
||
|
||
return satelliteInfo;
|
||
#else
|
||
return null;
|
||
#endif //FEATURE_FUSION
|
||
|
||
}
|
||
#endif // RESOURCE_SATELLITE_CONFIG
|
||
|
||
internal class ResourceManagerMediator
|
||
{
|
||
private ResourceManager _rm;
|
||
|
||
internal ResourceManagerMediator(ResourceManager rm)
|
||
{
|
||
if (rm == null)
|
||
{
|
||
throw new ArgumentNullException("rm");
|
||
}
|
||
_rm = rm;
|
||
}
|
||
|
||
// NEEDED ONLY BY FILE-BASED
|
||
internal String ModuleDir
|
||
{
|
||
get { return _rm.moduleDir; }
|
||
}
|
||
|
||
// NEEDED BOTH BY FILE-BASED AND ----Y-BASED
|
||
internal Type LocationInfo
|
||
{
|
||
get { return _rm._locationInfo; }
|
||
}
|
||
|
||
internal Type UserResourceSet
|
||
{
|
||
get { return _rm._userResourceSet; }
|
||
}
|
||
|
||
internal String BaseNameField
|
||
{
|
||
get { return _rm.BaseNameField; }
|
||
}
|
||
|
||
internal CultureInfo NeutralResourcesCulture
|
||
{
|
||
get { return _rm._neutralResourcesCulture; }
|
||
set { _rm._neutralResourcesCulture = value; }
|
||
}
|
||
|
||
internal String GetResourceFileName(CultureInfo culture)
|
||
{
|
||
return _rm.GetResourceFileName(culture);
|
||
}
|
||
|
||
// NEEDED ONLY BY ----Y-BASED
|
||
internal bool LookedForSatelliteContractVersion
|
||
{
|
||
get { return _rm._lookedForSatelliteContractVersion; }
|
||
set { _rm._lookedForSatelliteContractVersion = value; }
|
||
}
|
||
|
||
internal Version SatelliteContractVersion
|
||
{
|
||
get { return _rm._satelliteContractVersion; }
|
||
set { _rm._satelliteContractVersion = value; }
|
||
}
|
||
|
||
internal Version ObtainSatelliteContractVersion(Assembly a)
|
||
{
|
||
return ResourceManager.GetSatelliteContractVersion(a);
|
||
}
|
||
|
||
internal UltimateResourceFallbackLocation FallbackLoc
|
||
{
|
||
get { return _rm.FallbackLocation; }
|
||
set { _rm._fallbackLoc = value; }
|
||
}
|
||
|
||
internal RuntimeAssembly CallingAssembly
|
||
{
|
||
get { return _rm.m_callingAssembly; }
|
||
}
|
||
|
||
internal RuntimeAssembly MainAssembly
|
||
{
|
||
get { return (RuntimeAssembly)_rm.MainAssembly; }
|
||
}
|
||
|
||
// this is weird because we have BaseNameField accessor above, but we're sticking
|
||
// with it for compat.
|
||
internal String BaseName
|
||
{
|
||
get { return _rm.BaseName; }
|
||
}
|
||
|
||
|
||
#if RESOURCE_SATELLITE_CONFIG
|
||
[System.Security.SecurityCritical] // auto-generated
|
||
internal bool TryLookingForSatellite(CultureInfo lookForCulture)
|
||
{
|
||
return _rm.TryLookingForSatellite(lookForCulture);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
}
|
||
}
|