using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Core.Common.Utils.Properties; using log4net; namespace Core.Common.Utils.Reflection { /// /// Utility class containing functions for retrieving specific information about a .net assembly. /// public static class AssemblyUtils { private enum MachineType : ushort { IMAGE_FILE_MACHINE_UNKNOWN = 0x0, IMAGE_FILE_MACHINE_AM33 = 0x1d3, IMAGE_FILE_MACHINE_AMD64 = 0x8664, IMAGE_FILE_MACHINE_ARM = 0x1c0, IMAGE_FILE_MACHINE_EBC = 0xebc, IMAGE_FILE_MACHINE_I386 = 0x14c, IMAGE_FILE_MACHINE_IA64 = 0x200, IMAGE_FILE_MACHINE_M32R = 0x9041, IMAGE_FILE_MACHINE_MIPS16 = 0x266, IMAGE_FILE_MACHINE_MIPSFPU = 0x366, IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466, IMAGE_FILE_MACHINE_POWERPC = 0x1f0, IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1, IMAGE_FILE_MACHINE_R4000 = 0x166, IMAGE_FILE_MACHINE_SH3 = 0x1a2, IMAGE_FILE_MACHINE_SH3DSP = 0x1a3, IMAGE_FILE_MACHINE_SH4 = 0x1a6, IMAGE_FILE_MACHINE_SH5 = 0x1a8, IMAGE_FILE_MACHINE_THUMB = 0x1c2, IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169, } private static readonly ILog log = LogManager.GetLogger(typeof(AssemblyUtils)); /// /// Return attributes for a specific assembly /// /// /// public static AssemblyInfo GetAssemblyInfo(Assembly assembly) { AssemblyInfo info = new AssemblyInfo(); if (assembly.Location == "") { return info; } info.Version = assembly.GetName().Version.ToString(); var assemblyTitleAttribute = GetAssemblyAttributeValue(assembly); if (assemblyTitleAttribute != null) { info.Title = assemblyTitleAttribute.Title; } var assemblyDescriptionAttribute = GetAssemblyAttributeValue(assembly); if (assemblyDescriptionAttribute != null) { info.Description = assemblyDescriptionAttribute.Description; } var assemblyProductAttribute = GetAssemblyAttributeValue(assembly); if (assemblyProductAttribute != null) { info.Product = assemblyProductAttribute.Product; } var assemblyCopyrightAttribute = GetAssemblyAttributeValue(assembly); if (assemblyCopyrightAttribute != null) { info.Copyright = assemblyCopyrightAttribute.Copyright; } var assemblyCompanyAttribute = GetAssemblyAttributeValue(assembly); if (assemblyCompanyAttribute != null) { info.Company = assemblyCompanyAttribute.Company; } return info; } /// /// Return attributes for the executing assembly /// /// public static AssemblyInfo GetExecutingAssemblyInfo() { return GetAssemblyInfo(Assembly.GetExecutingAssembly()); } /// /// Gets types in a given assembly derived from a given type. /// /// /// /// public static IList GetDerivedTypes(Type baseType, Assembly assembly) { List types = new List(); // log.Debug(assembly.ToString()); try { foreach (Type t in assembly.GetTypes()) { if (t.IsSubclassOf(baseType)) { types.Add(t); } else if (t.GetInterface(baseType.ToString()) != null) { types.Add(t); } } } catch (Exception e) //hack because of unregistered ocx files TOOLS-518 { log.Debug(e); } return types; } /// /// Gets types derived from a given type. Searches all assemblies in a current application domain. /// /// /// public static IList GetDerivedTypes(Type baseType) { List types = new List(); IList assemblies = new List(AppDomain.CurrentDomain.GetAssemblies()); foreach (Assembly a in assemblies) { types.AddRange(GetDerivedTypes(baseType, a)); } return types; } /// /// Returns the type based on the full type name. Throws an exception if the types /// is found in multiple assemblies /// /// /// public static Type GetTypeByName(string typeName) { Type result = null; foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) { Type foundType = a.GetType(typeName); if (foundType != null) { if (result == null) { result = foundType; } else { throw new Exception("Type found in multiple assemblies"); } } } return result; } public static IEnumerable GetAssemblyResourceStreams(Assembly assembly, Func resourceNameFilter) { return from resourceName in assembly.GetManifestResourceNames() where resourceNameFilter(resourceName) select assembly.GetManifestResourceStream(resourceName); } public static Stream GetAssemblyResourceStream(Assembly assembly, string fileName) { return assembly.GetManifestResourceNames().Where(resourceName => resourceName.EndsWith(fileName)).Select( assembly.GetManifestResourceStream).First(); } /// /// This method checks if a file is a managed dll. It's based on how the file command on linux works, as explained in http://www.darwinsys.com/file/ /// /// path of dll /// true if file is a managed dll public static bool IsManagedDll(string path) { if (!path.EndsWith(".dll", StringComparison.Ordinal) && !path.EndsWith(".exe", StringComparison.Ordinal)) { return false; } // HACK: skip python dlls somehow they look like .NET assemblies if (path.Contains("ic_msvcr90.dll") || path.Contains("python26.dll")) { return false; } FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); fs.Seek(0x3C, SeekOrigin.Begin); // PE, SizeOfHeaders starts at 0x3B, second byte is 0x80 for .NET int i1 = fs.ReadByte(); fs.Seek(0x86, SeekOrigin.Begin); // 0x03 for managed code int i2 = fs.ReadByte(); fs.Close(); var isManagedDll = i1 == 0x80 && i2 == 0x03; //Debug.WriteLine(path + ": " + (isManagedDll?"managed":"unmanaged")); return isManagedDll; } public static bool Is64BitDll(string path) { if (IsManagedDll(path)) { var assemblyName = AssemblyName.GetAssemblyName(path); return assemblyName.ProcessorArchitecture == ProcessorArchitecture.Amd64 || assemblyName.ProcessorArchitecture == ProcessorArchitecture.IA64; } switch (GetDllMachineType(path)) { case MachineType.IMAGE_FILE_MACHINE_AMD64: case MachineType.IMAGE_FILE_MACHINE_IA64: return true; case MachineType.IMAGE_FILE_MACHINE_I386: return false; default: return false; } } public static IEnumerable LoadAllAssembliesFromDirectory(string path) { foreach (string filename in Directory.GetFiles(path).Where(name => name.EndsWith(".dll"))) { if (!IsManagedDll(filename)) { continue; } var assemblyName = Path.GetFileNameWithoutExtension(filename); //log.DebugFormat("Loading {0}", filename); Assembly a; try { a = Assembly.LoadFrom(filename); } catch (Exception exception) { log.ErrorFormat(Resource.AssemblyUtils_LoadAllAssembliesFromDirectory_Could_not_read_assembly_information_for_0_1_, assemblyName, exception.Message); continue; } yield return a; } } private static T GetAssemblyAttributeValue(Assembly assembly) where T : class { object[] attributes = assembly.GetCustomAttributes(typeof(T), true); if (attributes.Length == 0) { return null; } return (T) attributes[0]; } private static MachineType GetDllMachineType(string dllPath) { //see http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx //offset to PE header is always at 0x3C //PE header starts with "PE\0\0" = 0x50 0x45 0x00 0x00 //followed by 2-byte machine type field (see document above for enum) var fs = new FileStream(dllPath, FileMode.Open, FileAccess.Read); var br = new BinaryReader(fs); fs.Seek(0x3c, SeekOrigin.Begin); var peOffset = br.ReadInt32(); fs.Seek(peOffset, SeekOrigin.Begin); var peHead = br.ReadUInt32(); if (peHead != 0x00004550) // "PE\0\0", little-endian { throw new Exception("Can't find PE header"); } var machineType = (MachineType) br.ReadUInt16(); br.Close(); fs.Close(); return machineType; } #region Nested type: AssemblyInfo /// /// structure containing assembly attributes as strings. /// [Serializable] public struct AssemblyInfo { public string Company; public string Copyright; public string Description; public string Product; public string Title; public string Version; } #endregion } }