1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 using System
.Globalization
;
7 using System
.Reflection
;
8 using System
.Diagnostics
;
9 using Internal
.Resources
;
11 namespace System
.Resources
13 public partial class ResourceManager
15 private WindowsRuntimeResourceManagerBase
? _WinRTResourceManager
;
16 private PRIExceptionInfo
? _PRIExceptionInfo
;
17 private bool _PRIInitialized
;
18 private bool _useUapResourceManagement
;
20 private string? GetStringFromPRI(string stringName
, CultureInfo
? culture
, string? neutralResourcesCulture
)
22 Debug
.Assert(_useUapResourceManagement
);
23 Debug
.Assert(_WinRTResourceManager
!= null);
24 Debug
.Assert(_PRIInitialized
);
26 // If the caller explicitly passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
27 // null it out, so that we re-compute it. If we use modern resource lookup, we may end up getting a "better"
28 // match, since CultureInfo objects can't represent all the different languages the Uap resource model supports.
29 if (object.ReferenceEquals(culture
, CultureInfo
.CurrentUICulture
))
34 string? startingCulture
= culture
?.Name
;
38 // Always throw if we did not fully succeed in initializing the WinRT Resource Manager.
40 if (_PRIExceptionInfo
!= null && _PRIExceptionInfo
.PackageSimpleName
!= null && _PRIExceptionInfo
.ResWFile
!= null)
41 throw new MissingManifestResourceException(SR
.Format(SR
.MissingManifestResource_ResWFileNotLoaded
, _PRIExceptionInfo
.ResWFile
, _PRIExceptionInfo
.PackageSimpleName
));
43 throw new MissingManifestResourceException(SR
.MissingManifestResource_NoPRIresources
);
46 if (stringName
.Length
== 0)
49 // Do not handle exceptions. See the comment in SetUapConfiguration about throwing
50 // exception types that the ResourceManager class is not documented to throw.
51 return _WinRTResourceManager
.GetString(
53 string.IsNullOrEmpty(startingCulture
) ? null : startingCulture
,
54 string.IsNullOrEmpty(neutralResourcesCulture
) ? null : neutralResourcesCulture
);
57 // Since we can't directly reference System.Runtime.WindowsRuntime from System.Private.CoreLib, we have to get the type via reflection.
58 // It would be better if we could just implement WindowsRuntimeResourceManager in System.Private.CoreLib, but we can't, because
59 // we can do very little with WinRT in System.Private.CoreLib.
60 internal static WindowsRuntimeResourceManagerBase
GetWinRTResourceManager()
63 Type WinRTResourceManagerType
= Type
.GetType("System.Resources.WindowsRuntimeResourceManager, System.Runtime.WindowsRuntime", throwOnError
: true)!;
65 Assembly hiddenScopeAssembly
= Assembly
.Load(Internal
.Runtime
.Augments
.RuntimeAugments
.HiddenScopeAssemblyName
);
66 Type WinRTResourceManagerType
= hiddenScopeAssembly
.GetType("System.Resources.WindowsRuntimeResourceManager", true);
68 return (WindowsRuntimeResourceManagerBase
)Activator
.CreateInstance(WinRTResourceManagerType
, nonPublic
: true)!;
71 // CoreCLR: When running under AppX, the following rules apply for resource lookup:
73 // 1) For Framework assemblies, we always use satellite assembly based lookup.
74 // 2) For non-FX assemblies:
76 // a) If the assembly lives under PLATFORM_RESOURCE_ROOTS (as specified by the host during AppDomain creation),
77 // then we will use satellite assembly based lookup in assemblies like *.resources.dll.
79 // b) For any other non-FX assembly, we will use the modern resource manager with the premise that app package
80 // contains the PRI resources.
82 // .NET Native: If it is framework assembly we'll return true. The reason is in .NetNative we don't merge the
83 // resources to the app PRI file.
84 // The framework assemblies are tagged with attribute [assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
85 private static bool ShouldUseUapResourceManagement(Assembly resourcesAssembly
)
87 if (resourcesAssembly
== typeof(object).Assembly
) // We are not loading resources for System.Private.CoreLib
91 // Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
92 string? platformResourceRoots
= (string?)AppContext
.GetData("PLATFORM_RESOURCE_ROOTS");
93 if (!string.IsNullOrEmpty(platformResourceRoots
))
95 string resourceAssemblyPath
= resourcesAssembly
.Location
;
97 // Loop through the PLATFORM_RESOURCE_ROOTS and see if the assembly is contained in it.
98 foreach (string pathPlatformResourceRoot
in platformResourceRoots
.Split(Path
.PathSeparator
))
100 if (resourceAssemblyPath
.StartsWith(pathPlatformResourceRoot
, StringComparison
.CurrentCultureIgnoreCase
))
102 // Found the resource assembly to be present in one of the PLATFORM_RESOURCE_ROOT, so stop the enumeration loop.
107 #else // ENABLE_WINRT
108 foreach (var attrib
in resourcesAssembly
.GetCustomAttributes())
110 AssemblyMetadataAttribute
? meta
= attrib
as AssemblyMetadataAttribute
;
111 if (meta
!= null && meta
.Key
.Equals(".NETFrameworkAssembly"))
121 // Only call SetUapConfiguration from ResourceManager constructors, and nowhere else.
122 // Throws MissingManifestResourceException and WinRT HResults
123 private void SetUapConfiguration()
125 Debug
.Assert(!_useUapResourceManagement
); // Only this function writes to this member
126 Debug
.Assert(_WinRTResourceManager
== null); // Only this function writes to this member
127 Debug
.Assert(!_PRIInitialized
); // Only this function writes to this member
128 Debug
.Assert(_PRIExceptionInfo
== null); // Only this function writes to this member
131 if (!ApplicationModel
.IsUap
)
133 #else // ENABLE_WINRT
134 Internal
.Runtime
.Augments
.WinRTInteropCallbacks callbacks
= Internal
.Runtime
.Augments
.WinRTInterop
.UnsafeCallbacks
;
135 if (!(callbacks
!= null && callbacks
.IsAppxModel()))
139 Debug
.Assert(MainAssembly
!= null);
140 if (!ShouldUseUapResourceManagement(MainAssembly
))
143 _useUapResourceManagement
= true;
145 // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField.
146 string? reswFilename
= _locationInfo
== null ? BaseNameField
: _locationInfo
.FullName
;
148 // The only way this can happen is if a class inherited from ResourceManager and
149 // did not set the BaseNameField before calling the protected ResourceManager() constructor.
150 // For other constructors, we would already have thrown an ArgumentNullException by now.
151 // Throwing an ArgumentNullException now is not the right thing to do because technically
152 // ResourceManager() takes no arguments, and because it is not documented as throwing
153 // any exceptions. Instead, let's go through the rest of the initialization with this set to
154 // an empty string. We may in fact fail earlier for another reason, but otherwise we will
155 // throw a MissingManifestResourceException when GetString is called indicating that a
156 // resW filename called "" could not be found.
157 reswFilename
??= string.Empty
;
159 // At this point it is important NOT to set _useUapResourceManagement to false
160 // if the PRI file does not exist because we are now certain we need to load PRI
161 // resources. We want to fail by throwing a MissingManifestResourceException
162 // if WindowsRuntimeResourceManager.Initialize fails to locate the PRI file. We do not
163 // want to fall back to using satellite assemblies anymore. Note that we would not throw
164 // the MissingManifestResourceException from this function, but from GetString. See the
165 // comment below on the reason for this.
167 _WinRTResourceManager
= GetWinRTResourceManager();
171 _PRIInitialized
= _WinRTResourceManager
.Initialize(MainAssembly
.Location
, reswFilename
, out _PRIExceptionInfo
);
172 // Note that _PRIExceptionInfo might be null - this is OK.
173 // In that case we will just throw the generic
174 // MissingManifestResource_NoPRIresources exception.
175 // See the implementation of GetString for more details.
177 // We would like to be able to throw a MissingManifestResourceException here if PRI resources
178 // could not be loaded for a recognized reason. However, the ResourceManager constructors
179 // that call SetUapConfiguration are not documented as throwing MissingManifestResourceException,
180 // and since they are part of the portable profile, we cannot start throwing a new exception type
181 // as that would break existing portable libraries. Hence we must save the exception information
182 // now and throw the exception on the first call to GetString.
183 catch (FileNotFoundException
)
185 // We will throw MissingManifestResource_NoPRIresources from GetString
186 // when we see that _PRIInitialized is false.
190 // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps
191 // in WindowsRuntimeResourceManager.Initialize.
192 // In this case _PRIExceptionInfo is now null and we will just throw the generic
193 // MissingManifestResource_NoPRIresources exception.
194 // See the implementation of GetString for more details.
195 if (e
.HResult
!= HResults
.ERROR_MRM_MAP_NOT_FOUND
)
196 throw; // Unexpected exception code. Bubble it up to the caller.
199 if (!_PRIInitialized
)
201 _useUapResourceManagement
= false;
204 // Allow all other exception types to bubble up to the caller.
206 // Yes, this causes us to potentially throw exception types that are not documented.
208 // Ultimately the tradeoff is the following:
209 // -We could ignore unknown exceptions or rethrow them as inner exceptions
210 // of exceptions that the ResourceManager class is already documented as throwing.
211 // This would allow existing portable libraries to gracefully recover if they don't care
212 // too much about the ResourceManager object they are using. However it could
213 // mask potentially fatal errors that we are not aware of, such as a disk drive failing.
215 // The alternative, which we chose, is to throw unknown exceptions. This may tear
216 // down the process if the portable library and app don't expect this exception type.
217 // On the other hand, this won't mask potentially fatal errors we don't know about.