ResourceManager.Uap.cs 12 KB

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