/// <summary> /// Reads the assembly info from a file. /// This uses System.Reflection.Metadata, which is a very performant and low-level /// library. This is very convenient when scanning hundreds of DLLs at a time. /// </summary> /// <param name="filename">The full filename of the assembly.</param> /// <returns>The information about the assembly.</returns> public static AssemblyInfo ReadFromFile(string filename) { var result = new AssemblyInfo() { Filename = filename }; try { /* This method is significantly faster and more lightweight than using * System.Reflection.Assembly.ReflectionOnlyLoadFrom. It also allows * loading the same assembly from different locations. */ using (var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))) using (var sha1 = new SHA1CryptoServiceProvider()) { var metadata = pereader.GetMetadata(); unsafe { var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); var def = reader.GetAssemblyDefinition(); // This is how you compute the public key token from the full public key. // The last 8 bytes of the SHA1 of the public key. var publicKey = reader.GetBlobBytes(def.PublicKey); var publicKeyToken = sha1.ComputeHash(publicKey); var publicKeyString = new StringBuilder(); foreach (var b in publicKeyToken.Skip(12).Reverse()) { publicKeyString.AppendFormat("{0:x2}", b); } result.Name = reader.GetString(def.Name); result.Version = def.Version; result.Culture = def.Culture.IsNil ? "neutral" : reader.GetString(def.Culture); result.PublicKeyToken = publicKeyString.ToString(); result.Valid = true; } } } catch (BadImageFormatException) { // The DLL wasn't an assembly -> result.Valid = false. } catch (InvalidOperationException) { // Some other failure -> result.Valid = false. } return(result); }
/// <summary> /// Constructs the map from assembly string to its filename. /// /// Roslyn doesn't record the relationship between a filename and its assembly /// information, so we need to retrieve this information manually. /// </summary> private void SetReferencePaths() { foreach (var reference in compilation.References.OfType <PortableExecutableReference>()) { try { var refPath = reference.FilePath; /* This method is significantly faster and more lightweight than using * System.Reflection.Assembly.ReflectionOnlyLoadFrom. It is also allows * loading the same assembly from different locations. */ using var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(refPath, FileMode.Open, FileAccess.Read, FileShare.Read)); var metadata = pereader.GetMetadata(); string assemblyIdentity; unsafe { var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); var def = reader.GetAssemblyDefinition(); assemblyIdentity = reader.GetString(def.Name) + " " + def.Version; } extractor.SetAssemblyFile(assemblyIdentity, refPath); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { extractor.Message(new Message("Exception reading reference file", reference.FilePath, null, ex.StackTrace)); } } }