1 // 2 // BaseAssemblyResolver.cs 3 // 4 // Author: 5 // Jb Evain (jbevain@gmail.com) 6 // 7 // Copyright (c) 2008 - 2011 Jb Evain 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining 10 // a copy of this software and associated documentation files (the 11 // "Software"), to deal in the Software without restriction, including 12 // without limitation the rights to use, copy, modify, merge, publish, 13 // distribute, sublicense, and/or sell copies of the Software, and to 14 // permit persons to whom the Software is furnished to do so, subject to 15 // the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 // 28 29 using System; 30 using System.Collections.Generic; 31 using System.IO; 32 using System.Text; 33 34 using Mono.Collections.Generic; 35 36 namespace Mono.Cecil { 37 AssemblyResolveEventHandler(object sender, AssemblyNameReference reference)38 public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference); 39 40 public sealed class AssemblyResolveEventArgs : EventArgs { 41 42 readonly AssemblyNameReference reference; 43 44 public AssemblyNameReference AssemblyReference { 45 get { return reference; } 46 } 47 AssemblyResolveEventArgs(AssemblyNameReference reference)48 public AssemblyResolveEventArgs (AssemblyNameReference reference) 49 { 50 this.reference = reference; 51 } 52 } 53 54 #if !SILVERLIGHT && !CF 55 [Serializable] 56 #endif 57 public class AssemblyResolutionException : FileNotFoundException { 58 59 readonly AssemblyNameReference reference; 60 61 public AssemblyNameReference AssemblyReference { 62 get { return reference; } 63 } 64 AssemblyResolutionException(AssemblyNameReference reference)65 public AssemblyResolutionException (AssemblyNameReference reference) 66 : base (string.Format ("Failed to resolve assembly: '{0}'", reference)) 67 { 68 this.reference = reference; 69 } 70 71 #if !SILVERLIGHT && !CF AssemblyResolutionException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)72 protected AssemblyResolutionException ( 73 System.Runtime.Serialization.SerializationInfo info, 74 System.Runtime.Serialization.StreamingContext context) 75 : base (info, context) 76 { 77 } 78 #endif 79 } 80 81 public abstract class BaseAssemblyResolver : IAssemblyResolver { 82 83 static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null; 84 85 readonly Collection<string> directories; 86 87 #if !SILVERLIGHT && !CF 88 Collection<string> gac_paths; 89 #endif 90 AddSearchDirectory(string directory)91 public void AddSearchDirectory (string directory) 92 { 93 directories.Add (directory); 94 } 95 RemoveSearchDirectory(string directory)96 public void RemoveSearchDirectory (string directory) 97 { 98 directories.Remove (directory); 99 } 100 GetSearchDirectories()101 public string [] GetSearchDirectories () 102 { 103 var directories = new string [this.directories.size]; 104 Array.Copy (this.directories.items, directories, directories.Length); 105 return directories; 106 } 107 Resolve(string fullName)108 public virtual AssemblyDefinition Resolve (string fullName) 109 { 110 return Resolve (fullName, new ReaderParameters ()); 111 } 112 Resolve(string fullName, ReaderParameters parameters)113 public virtual AssemblyDefinition Resolve (string fullName, ReaderParameters parameters) 114 { 115 if (fullName == null) 116 throw new ArgumentNullException ("fullName"); 117 118 return Resolve (AssemblyNameReference.Parse (fullName), parameters); 119 } 120 121 public event AssemblyResolveEventHandler ResolveFailure; 122 BaseAssemblyResolver()123 protected BaseAssemblyResolver () 124 { 125 directories = new Collection<string> (2) { ".", "bin" }; 126 } 127 GetAssembly(string file, ReaderParameters parameters)128 AssemblyDefinition GetAssembly (string file, ReaderParameters parameters) 129 { 130 if (parameters.AssemblyResolver == null) 131 parameters.AssemblyResolver = this; 132 133 return ModuleDefinition.ReadModule (file, parameters).Assembly; 134 } 135 Resolve(AssemblyNameReference name)136 public virtual AssemblyDefinition Resolve (AssemblyNameReference name) 137 { 138 return Resolve (name, new ReaderParameters ()); 139 } 140 Resolve(AssemblyNameReference name, ReaderParameters parameters)141 public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) 142 { 143 if (name == null) 144 throw new ArgumentNullException ("name"); 145 if (parameters == null) 146 parameters = new ReaderParameters (); 147 148 var assembly = SearchDirectory (name, directories, parameters); 149 if (assembly != null) 150 return assembly; 151 152 #if !SILVERLIGHT && !CF 153 if (name.IsRetargetable) { 154 // if the reference is retargetable, zero it 155 name = new AssemblyNameReference (name.Name, new Version (0, 0, 0, 0)) { 156 PublicKeyToken = Empty<byte>.Array, 157 }; 158 } 159 160 var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName); 161 162 if (IsZero (name.Version)) { 163 assembly = SearchDirectory (name, new [] { framework_dir }, parameters); 164 if (assembly != null) 165 return assembly; 166 } 167 168 if (name.Name == "mscorlib") { 169 assembly = GetCorlib (name, parameters); 170 if (assembly != null) 171 return assembly; 172 } 173 174 assembly = GetAssemblyInGac (name, parameters); 175 if (assembly != null) 176 return assembly; 177 178 assembly = SearchDirectory (name, new [] { framework_dir }, parameters); 179 if (assembly != null) 180 return assembly; 181 #endif 182 183 if (ResolveFailure != null) { 184 assembly = ResolveFailure (this, name); 185 if (assembly != null) 186 return assembly; 187 } 188 189 throw new AssemblyResolutionException (name); 190 } 191 SearchDirectory(AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)192 AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters) 193 { 194 var extensions = new [] { ".exe", ".dll" }; 195 foreach (var directory in directories) { 196 foreach (var extension in extensions) { 197 string file = Path.Combine (directory, name.Name + extension); 198 if (File.Exists (file)) 199 return GetAssembly (file, parameters); 200 } 201 } 202 203 return null; 204 } 205 IsZero(Version version)206 static bool IsZero (Version version) 207 { 208 return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0); 209 } 210 211 #if !SILVERLIGHT && !CF GetCorlib(AssemblyNameReference reference, ReaderParameters parameters)212 AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters) 213 { 214 var version = reference.Version; 215 var corlib = typeof (object).Assembly.GetName (); 216 217 if (corlib.Version == version || IsZero (version)) 218 return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters); 219 220 var path = Directory.GetParent ( 221 Directory.GetParent ( 222 typeof (object).Module.FullyQualifiedName).FullName 223 ).FullName; 224 225 if (on_mono) { 226 if (version.Major == 1) 227 path = Path.Combine (path, "1.0"); 228 else if (version.Major == 2) { 229 if (version.MajorRevision == 5) 230 path = Path.Combine (path, "2.1"); 231 else 232 path = Path.Combine (path, "2.0"); 233 } else if (version.Major == 4) 234 path = Path.Combine (path, "4.0"); 235 else 236 throw new NotSupportedException ("Version not supported: " + version); 237 } else { 238 switch (version.Major) { 239 case 1: 240 if (version.MajorRevision == 3300) 241 path = Path.Combine (path, "v1.0.3705"); 242 else 243 path = Path.Combine (path, "v1.0.5000.0"); 244 break; 245 case 2: 246 path = Path.Combine (path, "v2.0.50727"); 247 break; 248 case 4: 249 path = Path.Combine (path, "v4.0.30319"); 250 break; 251 default: 252 throw new NotSupportedException ("Version not supported: " + version); 253 } 254 } 255 256 var file = Path.Combine (path, "mscorlib.dll"); 257 if (File.Exists (file)) 258 return GetAssembly (file, parameters); 259 260 return null; 261 } 262 GetGacPaths()263 static Collection<string> GetGacPaths () 264 { 265 if (on_mono) 266 return GetDefaultMonoGacPaths (); 267 268 var paths = new Collection<string> (2); 269 var windir = Environment.GetEnvironmentVariable ("WINDIR"); 270 if (windir == null) 271 return paths; 272 273 paths.Add (Path.Combine (windir, "assembly")); 274 paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly"))); 275 return paths; 276 } 277 GetDefaultMonoGacPaths()278 static Collection<string> GetDefaultMonoGacPaths () 279 { 280 var paths = new Collection<string> (1); 281 var gac = GetCurrentMonoGac (); 282 if (gac != null) 283 paths.Add (gac); 284 285 var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX"); 286 if (string.IsNullOrEmpty (gac_paths_env)) 287 return paths; 288 289 var prefixes = gac_paths_env.Split (Path.PathSeparator); 290 foreach (var prefix in prefixes) { 291 if (string.IsNullOrEmpty (prefix)) 292 continue; 293 294 var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac"); 295 if (Directory.Exists (gac_path) && !paths.Contains (gac)) 296 paths.Add (gac_path); 297 } 298 299 return paths; 300 } 301 GetCurrentMonoGac()302 static string GetCurrentMonoGac () 303 { 304 return Path.Combine ( 305 Directory.GetParent ( 306 Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName, 307 "gac"); 308 } 309 GetAssemblyInGac(AssemblyNameReference reference, ReaderParameters parameters)310 AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters) 311 { 312 if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) 313 return null; 314 315 if (gac_paths == null) 316 gac_paths = GetGacPaths (); 317 318 if (on_mono) 319 return GetAssemblyInMonoGac (reference, parameters); 320 321 return GetAssemblyInNetGac (reference, parameters); 322 } 323 GetAssemblyInMonoGac(AssemblyNameReference reference, ReaderParameters parameters)324 AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters) 325 { 326 for (int i = 0; i < gac_paths.Count; i++) { 327 var gac_path = gac_paths [i]; 328 var file = GetAssemblyFile (reference, string.Empty, gac_path); 329 if (File.Exists (file)) 330 return GetAssembly (file, parameters); 331 } 332 333 return null; 334 } 335 GetAssemblyInNetGac(AssemblyNameReference reference, ReaderParameters parameters)336 AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters) 337 { 338 var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; 339 var prefixes = new [] { string.Empty, "v4.0_" }; 340 341 for (int i = 0; i < 2; i++) { 342 for (int j = 0; j < gacs.Length; j++) { 343 var gac = Path.Combine (gac_paths [i], gacs [j]); 344 var file = GetAssemblyFile (reference, prefixes [i], gac); 345 if (Directory.Exists (gac) && File.Exists (file)) 346 return GetAssembly (file, parameters); 347 } 348 } 349 350 return null; 351 } 352 GetAssemblyFile(AssemblyNameReference reference, string prefix, string gac)353 static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac) 354 { 355 var gac_folder = new StringBuilder () 356 .Append (prefix) 357 .Append (reference.Version) 358 .Append ("__"); 359 360 for (int i = 0; i < reference.PublicKeyToken.Length; i++) 361 gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2")); 362 363 return Path.Combine ( 364 Path.Combine ( 365 Path.Combine (gac, reference.Name), gac_folder.ToString ()), 366 reference.Name + ".dll"); 367 } 368 #endif 369 } 370 } 371