// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; namespace Microsoft.DotNet.Tools.Test.Utilities { public static class PeReaderUtils { public static string GetAssemblyAttributeValue(string assemblyPath, string attributeName) { if (!File.Exists(assemblyPath)) { return null; } using (var stream = File.OpenRead(assemblyPath)) using (var peReader = new PEReader(stream)) { if (!peReader.HasMetadata) { return null; } var mdReader = peReader.GetMetadataReader(); var attrs = mdReader.GetAssemblyDefinition().GetCustomAttributes() .Select(ah => mdReader.GetCustomAttribute(ah)); foreach (var attr in attrs) { var ctorHandle = attr.Constructor; if (ctorHandle.Kind != HandleKind.MemberReference) { continue; } var container = mdReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; var name = mdReader.GetTypeReference((TypeReferenceHandle)container).Name; if (!string.Equals(mdReader.GetString(name), attributeName)) { continue; } var arguments = GetFixedStringArguments(mdReader, attr); if (arguments.Count == 1) { return arguments[0]; } } } return null; } /// /// Gets the fixed (required) string arguments of a custom attribute. /// Only attributes that have only fixed string arguments. /// private static List GetFixedStringArguments(MetadataReader reader, CustomAttribute attribute) { // TODO: Nick Guerrera (Nick.Guerrera@microsoft.com) hacked this method for temporary use. // There is a blob decoder feature in progress but it won't ship in time for our milestone. // Replace this method with the blob decoder feature when later it is availale. var signature = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor).Signature; var signatureReader = reader.GetBlobReader(signature); var valueReader = reader.GetBlobReader(attribute.Value); var arguments = new List(); var prolog = valueReader.ReadUInt16(); if (prolog != 1) { // Invalid custom attribute prolog return arguments; } var header = signatureReader.ReadSignatureHeader(); if (header.Kind != SignatureKind.Method || header.IsGeneric) { // Invalid custom attribute constructor signature return arguments; } int parameterCount; if (!signatureReader.TryReadCompressedInteger(out parameterCount)) { // Invalid custom attribute constructor signature return arguments; } var returnType = signatureReader.ReadSignatureTypeCode(); if (returnType != SignatureTypeCode.Void) { // Invalid custom attribute constructor signature return arguments; } for (int i = 0; i < parameterCount; i++) { var signatureTypeCode = signatureReader.ReadSignatureTypeCode(); if (signatureTypeCode == SignatureTypeCode.String) { // Custom attribute constructor must take only strings arguments.Add(valueReader.ReadSerializedString()); } } return arguments; } } }