/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Resources; namespace dnSpy.Contracts.Resources { /// /// Caches the info so we don't have to call which is very slow /// abstract class ResourceManagerTokenCache { public abstract bool TryGetResourceManagerGetMethodMetadataToken(Assembly assembly, out int getMethodMetadataToken); public abstract void SetResourceManagerGetMethodMetadataToken(Assembly assembly, int getMethodMetadataToken); } /// /// Converts strings to resource strings /// public static class ResourceHelper { const string PREFIX = "res:"; static ResourceManagerTokenCache? resourceManagerTokenCache; internal static void SetResourceManagerTokenCache(ResourceManagerTokenCache tokenCache) { if (tokenCache is null) throw new ArgumentNullException(nameof(tokenCache)); if (resourceManagerTokenCache is not null) throw new InvalidOperationException(); resourceManagerTokenCache = tokenCache; } /// /// Converts to a string in the resources if it has been prefixed with "res:" /// /// Can be any object in the assembly containing the resources or the assembly itself (). /// String /// public static string? GetStringOrNull(object obj, string? value) { Debug2.Assert(resourceManagerTokenCache is not null); if (obj is null) throw new ArgumentNullException(nameof(obj)); if (value is null) return value; return GetString(obj, value); } /// /// Converts to a string in the resources if it has been prefixed with "res:" /// /// Can be any object in the assembly containing the resources or the assembly itself (). /// String /// public static string GetString(object obj, string value) { Debug2.Assert(resourceManagerTokenCache is not null); if (obj is null) throw new ArgumentNullException(nameof(obj)); if (value is null) throw new ArgumentNullException(nameof(value)); if (!value.StartsWith(PREFIX)) return value; var key = value.Substring(PREFIX.Length); var mgr = GetResourceManager(obj as Assembly ?? obj.GetType().Assembly); if (mgr is null) return "???"; var s = mgr.GetString(key); Debug2.Assert(s is not null); return s ?? "???"; } static ResourceManager? GetResourceManager(Assembly assembly) { if (asmToMgr.TryGetValue(assembly, out var mgr)) return mgr; var tokenCache = resourceManagerTokenCache; Debug2.Assert(tokenCache is not null); if (tokenCache is not null) { if (tokenCache.TryGetResourceManagerGetMethodMetadataToken(assembly, out int getMethodMetadataToken)) { MethodInfo? method; try { method = assembly.ManifestModule.ResolveMethod(getMethodMetadataToken) as MethodInfo; } catch (ArgumentException) { Debug.Fail("Couldn't resolve resource manager getter method"); method = null; } mgr = TrySetResourceManager(assembly, method, save: false); if (mgr is not null) return mgr; } } foreach (var type in assembly.ManifestModule.GetTypes()) { if (type.Namespace is null || !type.Namespace.EndsWith(".Properties", StringComparison.InvariantCultureIgnoreCase)) continue; var prop = type.GetProperty("ResourceManager", BindingFlags.Public | BindingFlags.Static); if (prop is null) continue; var m = prop.GetGetMethod(); mgr = TrySetResourceManager(assembly, m, save: true); if (mgr is not null) return mgr; } Debug.Fail($"Failed to find the class with the ResourceManager property in assembly {assembly}"); return null; } static ResourceManager? TrySetResourceManager(Assembly assembly, MethodInfo? m, bool save) { if (m is null) return null; if (!m.IsStatic) return null; if (m.ReturnType != typeof(ResourceManager)) return null; if (m.GetParameters().Length != 0) return null; ResourceManager? mgr; try { mgr = m.Invoke(null, Array.Empty()) as ResourceManager; } catch { return null; } if (save) resourceManagerTokenCache?.SetResourceManagerGetMethodMetadataToken(assembly, m.MetadataToken); asmToMgr[assembly] = mgr; return mgr; } static readonly Dictionary asmToMgr = new Dictionary(); } }