1 //------------------------------------------------------------------------------ 2 // <copyright file="BitmapSelector.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 namespace System.Drawing { 8 using System; 9 using System.Configuration; 10 using System.Drawing.Configuration; 11 using System.IO; 12 using System.Reflection; 13 14 /// <summary> 15 /// Provides methods to select from multiple bitmaps depending on a "bitmapSuffix" config setting. 16 /// </summary> 17 internal static class BitmapSelector { 18 19 /// <summary> 20 /// Gets the bitmap ID suffix defined in the application configuration, or string.Empty if 21 /// the suffix is not specified. Internal for unit tests 22 /// </summary> 23 /// <remarks> 24 /// For performance, the suffix is cached in a static variable so it only has to be read 25 /// once per appdomain. 26 /// </remarks> 27 private static string _suffix; 28 internal static string Suffix { 29 get { 30 if (_suffix == null) { 31 _suffix = string.Empty; 32 var section = ConfigurationManager.GetSection("system.drawing") as SystemDrawingSection; 33 if (section != null) { 34 var value = section.BitmapSuffix; 35 if (value != null && value is string) { 36 _suffix = (string)value; 37 } 38 } 39 } 40 return _suffix; 41 } 42 set { 43 // So unit tests can clear the cached suffix 44 _suffix = value; 45 } 46 } 47 48 /// <summary> 49 /// Appends the current suffix to <paramref name="filePath"/>. The suffix is appended 50 /// before the existing extension (if any). Internal for unit tests. 51 /// </summary> 52 /// <returns> 53 /// The new path with the suffix included. If there is no suffix defined or there are 54 /// invalid characters in the original path, the original path is returned. 55 /// </returns> AppendSuffix(string filePath)56 internal static string AppendSuffix(string filePath) { 57 try { 58 return Path.ChangeExtension(filePath, Suffix + Path.GetExtension(filePath)); 59 } 60 catch (ArgumentException) { // there are invalid characters in the path 61 return filePath; 62 } 63 } 64 65 /// <summary> 66 /// Returns <paramref name="originalPath"/> with the current suffix appended (before the 67 /// existing extension) if the resulting file path exists; otherwise the original path is 68 /// returned. 69 /// </summary> GetFileName(string originalPath)70 public static string GetFileName(string originalPath) { 71 if (Suffix == string.Empty) 72 return originalPath; 73 74 string newPath = AppendSuffix(originalPath); 75 return File.Exists(newPath) ? newPath : originalPath; 76 } 77 78 // Calls assembly.GetManifestResourceStream in a try/catch and returns null if not found GetResourceStreamHelper(Assembly assembly, Type type, string name)79 private static Stream GetResourceStreamHelper(Assembly assembly, Type type, string name) { 80 Stream stream = null; 81 try { 82 stream = assembly.GetManifestResourceStream(type, name); 83 } 84 catch (FileNotFoundException) { 85 } 86 return stream; 87 } 88 DoesAssemblyHaveCustomAttribute(Assembly assembly, string typeName)89 private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, string typeName) { 90 return DoesAssemblyHaveCustomAttribute(assembly, assembly.GetType(typeName)); 91 } 92 DoesAssemblyHaveCustomAttribute(Assembly assembly, Type attrType)93 private static bool DoesAssemblyHaveCustomAttribute(Assembly assembly, Type attrType) { 94 if (attrType != null) { 95 var attr = assembly.GetCustomAttributes(attrType, false); 96 if (attr.Length > 0) { 97 return true; 98 } 99 } 100 return false; 101 } 102 103 // internal for unit tests SatelliteAssemblyOptIn(Assembly assembly)104 internal static bool SatelliteAssemblyOptIn(Assembly assembly) { 105 // Try 4.5 public attribute type first 106 if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSatelliteAssemblyAttribute))) { 107 return true; 108 } 109 110 // Also load attribute type by name for dlls compiled against older frameworks 111 return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute"); 112 } 113 114 // internal for unit tests SameAssemblyOptIn(Assembly assembly)115 internal static bool SameAssemblyOptIn(Assembly assembly) { 116 // Try 4.5 public attribute type first 117 if (DoesAssemblyHaveCustomAttribute(assembly, typeof(BitmapSuffixInSameAssemblyAttribute))) { 118 return true; 119 } 120 121 // Also load attribute type by name for dlls compiled against older frameworks 122 return DoesAssemblyHaveCustomAttribute(assembly, "System.Drawing.BitmapSuffixInSameAssemblyAttribute"); 123 } 124 125 /// <summary> 126 /// Returns a resource stream loaded from the appropriate location according to the current 127 /// suffix. 128 /// </summary> 129 /// <param name="assembly">The assembly from which the stream is loaded</param> 130 /// <param name="type">The type whose namespace is used to scope the manifest resource name</param> 131 /// <param name="originalName">The name of the manifest resource being requested</param> 132 /// <returns> 133 /// The manifest resource stream corresponding to <paramref name="originalName"/> with the 134 /// current suffix applied; or if that is not found, the stream corresponding to <paramref name="originalName"/>. 135 /// </returns> GetResourceStream(Assembly assembly, Type type, string originalName)136 public static Stream GetResourceStream(Assembly assembly, Type type, string originalName) { 137 if (Suffix != string.Empty) { 138 try { 139 // Resource with suffix has highest priority 140 if (SameAssemblyOptIn(assembly)) { 141 string newName = AppendSuffix(originalName); 142 Stream stream = GetResourceStreamHelper(assembly, type, newName); 143 if (stream != null) { 144 return stream; 145 } 146 } 147 } 148 catch { 149 // Ignore failures and continue to try other options 150 } 151 152 try { 153 // Satellite assembly has second priority, using the original name 154 if (SatelliteAssemblyOptIn(assembly)) { 155 AssemblyName assemblyName = assembly.GetName(); 156 assemblyName.Name += Suffix; 157 assemblyName.ProcessorArchitecture = ProcessorArchitecture.None; 158 Assembly satellite = Assembly.Load(assemblyName); 159 if (satellite != null) { 160 Stream stream = GetResourceStreamHelper(satellite, type, originalName); 161 if (stream != null) { 162 return stream; 163 } 164 } 165 } 166 } 167 catch { 168 // Ignore failures and continue to try other options 169 } 170 } 171 172 // Otherwise fall back to specified assembly and original name requested 173 return assembly.GetManifestResourceStream(type, originalName); 174 } 175 176 /// <summary> 177 /// Returns a resource stream loaded from the appropriate location according to the current 178 /// suffix. 179 /// </summary> 180 /// <param name="type">The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name</param> 181 /// <param name="originalName">The name of the manifest resource being requested</param> 182 /// <returns> 183 /// The manifest resource stream corresponding to <paramref name="originalName"/> with the 184 /// current suffix applied; or if that is not found, the stream corresponding to <paramref name="originalName"/>. 185 /// </returns> GetResourceStream(Type type, string originalName)186 public static Stream GetResourceStream(Type type, string originalName) { 187 return GetResourceStream(type.Module.Assembly, type, originalName); 188 } 189 190 /// <summary> 191 /// Returns an Icon created from a resource stream loaded from the appropriate location according to the current 192 /// suffix. 193 /// </summary> 194 /// <param name="type">The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name</param> 195 /// <param name="originalName">The name of the manifest resource being requested</param> 196 /// <returns> 197 /// The icon created from a manifest resource stream corresponding to <paramref name="originalName"/> with the 198 /// current suffix applied; or if that is not found, the stream corresponding to <paramref name="originalName"/>. 199 /// </returns> CreateIcon(Type type, string originalName)200 public static Icon CreateIcon(Type type, string originalName) { 201 return new Icon(GetResourceStream(type, originalName)); 202 } 203 204 /// <summary> 205 /// Returns an Bitmap created from a resource stream loaded from the appropriate location according to the current 206 /// suffix. 207 /// </summary> 208 /// <param name="type">The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name</param> 209 /// <param name="originalName">The name of the manifest resource being requested</param> 210 /// <returns> 211 /// The bitmap created from a manifest resource stream corresponding to <paramref name="originalName"/> with the 212 /// current suffix applied; or if that is not found, the stream corresponding to <paramref name="originalName"/>. 213 /// </returns> CreateBitmap(Type type, string originalName)214 public static Bitmap CreateBitmap(Type type, string originalName) { 215 return new Bitmap(GetResourceStream(type, originalName)); 216 } 217 218 } 219 } 220