// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reflection; using System.IO; using System.Data; namespace Spludlow { public class Reflections { public static object Invoke(string assemblyName, string typeName, string methodName) { return Invoke(null, assemblyName, typeName, methodName, null, null); } public static object Invoke(string assemblyName, string typeName, string methodName, object[] parameters) { return Invoke(null, assemblyName, typeName, methodName, parameters, null); } public static object Invoke(string assemblyName, string typeName, string methodName, object[] parameters, object[] constructorArguments) { return Invoke(null, assemblyName, typeName, methodName, parameters, constructorArguments); } public static object Invoke(object instance, string assemblyName, string typeName, string methodName) { return Invoke(instance, assemblyName, typeName, methodName, null, null); } public static object Invoke(object instance, string assemblyName, string typeName, string methodName, object[] parameters) { return Invoke(instance, assemblyName, typeName, methodName, parameters, null); } public static object Invoke(object instance, string assemblyName, string typeName, string methodName, object[] parameters, object[] constructorArguments) { Type type = FindType(assemblyName, typeName); MethodInfo methodInfo = FindMethodOrProperty(type, methodName, parameters); if (instance == null) instance = MakeInstance(type, methodInfo, constructorArguments); return Invoke(instance, methodInfo, parameters); } public static object Invoke(object instance, MethodInfo methodInfo, object[] parameters) { try { return methodInfo.Invoke(instance, parameters); } catch (TargetInvocationException ee) { if (ee.InnerException != null) throw ee.InnerException; else throw ee; } } public static Type FindType(string assemblyName, string typeName) { // Available as standard Type type = Type.GetType(typeName, false, false); if (type != null) return type; Assembly assembly = FindAssembly(assemblyName); if (assembly == null) throw new ApplicationException("Reflections; FindType, Assembly not found:\t" + assemblyName); try { return assembly.GetType(typeName, true, false); } catch (Exception ee) { throw new ApplicationException("Reflections; FindType, Type not found:\t" + assembly.Location + "\t" + typeName, ee); } } public static Assembly FindAssembly(string assemblyName) { Assembly assembly = null; string info = ""; // Full path specified if (assemblyName.EndsWith(".dll") == true) { assembly = Assembly.LoadFrom(assemblyName); if (assembly != null) info = "Full path specified"; } // Look in CurrentDomain if (assembly == null) { assembly = MatchAssembly(assemblyName, AppDomain.CurrentDomain.GetAssemblies()); if (assembly != null) info = "Current Domain"; } // Look if dll adjacent or referenced from standard assembiles if (assembly == null) { Dictionary standardAssembiles = new Dictionary(); Assembly standardAssembly; standardAssembly = Assembly.GetCallingAssembly(); if (standardAssembly != null) standardAssembiles.Add("CallingAssembly", standardAssembly); standardAssembly = Assembly.GetEntryAssembly(); if (standardAssembly != null) standardAssembiles.Add("EntryAssembly", standardAssembly); standardAssembly = Assembly.GetExecutingAssembly(); if (standardAssembly != null) standardAssembiles.Add("ExecutingAssembly", standardAssembly); for (int pass = 0; pass < 2; ++pass) { foreach (string assemblyPlaceName in standardAssembiles.Keys) { standardAssembly = standardAssembiles[assemblyPlaceName]; if (pass == 0) assembly = LocateAssembly(assemblyName, standardAssembly); else assembly = MatchReferencedAssembly(assemblyName, standardAssembly); if (assembly != null) { if (pass == 0) info = "Located Assembly next to "; else info = "Referenced Assembly from "; info += assemblyPlaceName; pass = 2; break; } } } } // Look for as Spludlow Application if (assembly == null) { string applicationDirectory = Spludlow.Config.ApplicationDirectory(assemblyName, true); if (applicationDirectory != null) { string assemblyPath = applicationDirectory + @"\" + assemblyName + ".dll"; if (File.Exists(assemblyPath) == true) assembly = Assembly.LoadFrom(assemblyPath); if (assembly != null) info = "Spludlow Application"; } } // Look in Program Files if (assembly == null) { string applicationDirectory = Spludlow.Config.ApplicationDirectory("Spludlow-Process", true); // Should always be present if (applicationDirectory != null) { string programFilesDirectory = Path.GetDirectoryName(applicationDirectory); string assemblyPath = programFilesDirectory + @"\" + assemblyName; if (Directory.Exists(assemblyPath) == true) { assemblyPath += @"\" + assemblyName + ".dll"; if (File.Exists(assemblyPath) == true) assembly = Assembly.LoadFrom(assemblyPath); if (assembly != null) info = "Program Files"; } } } // Look in Current Directory if (assembly == null) { string assemblyPath = Path.GetFullPath(assemblyName + ".dll"); if (File.Exists(assemblyPath) == true) assembly = Assembly.LoadFrom(assemblyPath); if (assembly != null) info = "Current Directory"; } string assemblyLocation = ""; if (assembly != null && assembly.Location != null) assemblyLocation = assembly.Location; return assembly; } private static Assembly MatchAssembly(string assemblyName, Assembly[] assemblies) { Assembly found = null; foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == assemblyName) if (found == null) found = assembly; else throw new ApplicationException("Reflections; MatchAssembly Finding Type, Multiple Assembly Name matched:\t" + assemblyName); } return found; } private static Assembly MatchReferencedAssembly(string assemblyName, Assembly assembly) { Assembly found = null; foreach (AssemblyName assName in assembly.GetReferencedAssemblies()) { if (assName.Name == assemblyName) { if (found == null) { found = Assembly.Load(assName); if (found == null) throw new ApplicationException("Reflections; MatchReferencedAssembly, Assembly Load failed:\t" + assembly.FullName + "\t" + assName.Name + "\t" + assemblyName); } else { throw new ApplicationException("Reflections; MatchReferencedAssembly, Multiple Assembly matched:\t" + assemblyName); } } } return found; } private static Assembly LocateAssembly(string assemblyName, Assembly adjacentAssembly) { string assemblyPath = Path.GetDirectoryName(adjacentAssembly.Location) + @"\" + assemblyName + ".dll"; if (File.Exists(assemblyPath) == true) return Assembly.LoadFrom(assemblyPath); return null; } public static object MakeInstance(string assemblyName, string typeName) { return MakeInstance(assemblyName, typeName, null); } public static object MakeInstance(string assemblyName, string typeName, object[] constructorArguments) { Type type = FindType(assemblyName, typeName); ConstructorInfo constructorInfo = FindConstructor(type, constructorArguments); if (constructorInfo == null) throw new ApplicationException("Reflections; MakeInstance, Constructor not found:\t" + assemblyName + "\t" + typeName); return constructorInfo.Invoke(constructorArguments); } public static object MakeInstance(Type type, MethodInfo methodInfo, object[] constructorArguments) { object instance = null; if (methodInfo.IsStatic == false) { ConstructorInfo constructorInfo = FindConstructor(type, constructorArguments); if (constructorInfo == null) throw new ApplicationException("Reflections; MakeInstance, Constructor not found:\t" + type.Assembly.FullName + "\t" + type.Name); instance = constructorInfo.Invoke(constructorArguments); } else { if (constructorArguments != null && constructorArguments.Length > 0) throw new ApplicationException("Reflections; MakeInstance, Constructor Arguments not permitted with Static Method or Property:\t" + type.Assembly.FullName + "\t" + type.Name); } return instance; } public static MethodInfo FindMethodOrProperty(Type type, string methodName, object[] parameters) { MethodInfo methodInfo = FindMethod(type, methodName, parameters); if (methodInfo == null) { PropertyInfo propertyInfo = type.GetProperty(methodName); if (propertyInfo == null) throw new ApplicationException("Reflections; FindMethodOrProperty, Method or Property not found:\t" + type.Name + "\t" + methodName); if (parameters.Length != 0) throw new ApplicationException("Reflections; FindMethodOrProperty, Parameters not permitted with Invoke Property:\t" + type.Name + "\t" + methodName); methodInfo = propertyInfo.GetGetMethod(); if (methodInfo == null) throw new ApplicationException("Reflections; FindMethodOrProperty, Property Get Method not found:\t" + type.Name + "\t" + methodName); } return methodInfo; } public static MethodInfo FindMethod(Type type, string methodName, object[] parameters) { return (MethodInfo)FindMember(type, methodName, MemberTypes.Method, parameters); } public static MethodInfo FindMethod(Type type, string methodName, Type[] types) { return (MethodInfo)FindMember(type, methodName, MemberTypes.Method, types); } public static ConstructorInfo FindConstructor(Type type, object[] parameters) { return (ConstructorInfo)FindMember(type, null, MemberTypes.Constructor, parameters); } public static ConstructorInfo FindConstructor(Type type, Type[] types) { return (ConstructorInfo)FindMember(type, null, MemberTypes.Constructor, types); } public static MemberInfo FindMember(Type type, string methodName, MemberTypes memberTypes, object[] parameters) { Type[] types = null; if (parameters != null) { types = new Type[parameters.Length]; for (int index = 0; index < parameters.Length; ++index) if (parameters[index] != null) types[index] = parameters[index].GetType(); } return FindMember(type, methodName, memberTypes, types); } public static MemberInfo FindMember(Type type, string methodName, MemberTypes memberTypes, Type[] types) { if (types == null) types = new Type[0]; MemberInfo foundMemberInfo = null; MemberInfo[] memberInfos = type.FindMembers(memberTypes, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, new MemberFilter(MemberMatch), methodName); foreach (MemberInfo memberInfo in memberInfos) { ParameterInfo[] methodParameters = ParameterInfos(memberInfo); if (methodParameters.Length != types.Length) continue; bool match = true; for (int index = 0; index < types.Length; ++index) { if (types[index] == null) continue; if (types[index] != methodParameters[index].ParameterType) { match = false; break; } } if (match == true) { if (foundMemberInfo == null) foundMemberInfo = memberInfo; else throw new ApplicationException("Reflections; FindMember, Ambiguous Method matched on parameters:\t" + type.Name + "\t" + methodName + "\t" + memberTypes.ToString()); } } return foundMemberInfo; } private static bool MemberMatch(MemberInfo memberInfo, object name) { if (name == null || memberInfo.Name == (string)name) return true; return false; } private static ParameterInfo[] ParameterInfos(MemberInfo memberInfo) { ParameterInfo[] methodParameters; if (memberInfo is MethodInfo) methodParameters = ((MethodInfo)memberInfo).GetParameters(); else if (memberInfo is ConstructorInfo) methodParameters = ((ConstructorInfo)memberInfo).GetParameters(); else throw new ApplicationException("Reflections; ParameterInfos, MemberInfo was not MethodInfo or ConstructorInfo:\t" + memberInfo.MemberType.ToString()); return methodParameters; } private static DataTable NewMethodsTable() { return Spludlow.Data.TextTable.ReadText(new string[] { "Id Assembly Type Method ParameterText Parameters ReturnText ReturnType IsStatic IsConstructor CheckSum", "Int32* String String String String String[][] String String Boolean Boolean String", }); } public static DataSet Search(string host, string searchText) { return (DataSet)Spludlow.Call.Now(host, "Spludlow", "Spludlow.Reflections", "Search", new object[] { searchText }, CallFlags.LargeResult); } public static DataSet Search(string searchText) { searchText = searchText.ToLower(); string[] searchWords = Spludlow.Text.Split(searchText, ' ', true); DataTable methodsTable = NewMethodsTable(); foreach (Assembly assembly in InterestedAssemblies()) { string assemblyName = assembly.GetName().Name; foreach (Type type in assembly.GetTypes()) { if (type.IsInterface == true) continue; if (type.BaseType != null) { if (type.BaseType == typeof(System.Data.DataSet)) continue; if (type.BaseType == typeof(System.ComponentModel.Component)) continue; } if (type.DeclaringType != null && type.DeclaringType.BaseType != null) { if (type.DeclaringType.BaseType == typeof(System.Data.DataSet)) continue; } bool match = false; bool typeNameMatch = Spludlow.Text.MatchWords(type.FullName.ToLower(), searchWords); MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); foreach (MethodInfo method in methods) { if (typeNameMatch == true || Spludlow.Text.MatchWords(method.Name.ToLower(), searchWords) == true) { AddMember(methodsTable, assemblyName, type, method); match = true; } } if (match == true) { foreach (ConstructorInfo constructor in type.GetConstructors()) AddMember(methodsTable, assemblyName, type, constructor); } } } return Spludlow.Data.ADO.WireDataSet(methodsTable); } public static DataSet MethodMembers(string assemblyName, string typeName, string methodName) { DataTable methodsTable = NewMethodsTable(); foreach (Assembly assembly in InterestedAssemblies()) { if (assembly.GetName().Name != assemblyName) continue; foreach (Type type in assembly.GetTypes()) { if (type.FullName != typeName) continue; if (type.IsInterface == true) continue; if (type.BaseType != null) { if (type.BaseType == typeof(System.Data.DataSet)) continue; if (type.BaseType == typeof(System.ComponentModel.Component)) continue; } if (type.DeclaringType != null && type.DeclaringType.BaseType != null) { if (type.DeclaringType.BaseType == typeof(System.Data.DataSet)) continue; } bool match = false; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); foreach (MethodInfo method in methods) { if (method.Name == methodName) { AddMember(methodsTable, assemblyName, type, method); match = true; } } if (match == true) { foreach (ConstructorInfo constructor in type.GetConstructors()) AddMember(methodsTable, assemblyName, type, constructor); } } } DataSet dataSet = new DataSet(); dataSet.Tables.Add(methodsTable); return dataSet; } private static int AddMember(DataTable table, string assemblyName, Type type, MemberInfo member) { string returnText = ""; string returnType = ""; bool isStatic; bool isConstructor; if (member is MethodInfo) { returnText = ((MethodInfo)member).ReturnType.Name; returnType = ((MethodInfo)member).ReturnType.AssemblyQualifiedName; isStatic = ((MethodInfo)member).IsStatic; isConstructor = ((MethodInfo)member).IsConstructor; } else { if (member is ConstructorInfo) { isStatic = ((ConstructorInfo)member).IsStatic; isConstructor = ((ConstructorInfo)member).IsConstructor; } else { throw new ApplicationException("MemberInfo was not MethodInfo or ConstructorInfo:\t" + member.MemberType.ToString()); } } int id = table.Rows.Count + 1; StringBuilder paramText = new StringBuilder(); List parameters = new List(); foreach (ParameterInfo paramInfo in ParameterInfos(member)) { if (paramText.Length > 0) paramText.Append(", "); paramText.Append(paramInfo.ParameterType.Name); paramText.Append(" "); paramText.Append(paramInfo.Name); parameters.Add(new string[] { paramInfo.ParameterType.AssemblyQualifiedName, null, paramInfo.Name }); } string parameterText = paramText.ToString(); StringBuilder checkSumData = new StringBuilder(); checkSumData.AppendLine(type.AssemblyQualifiedName); checkSumData.AppendLine(parameterText); string checkSum = Spludlow.Hashing.MD5Hex(checkSumData.ToString()); table.Rows.Add(new object[] { id, assemblyName, type.FullName, member.Name, parameterText, parameters.ToArray(), returnText, returnType, isStatic, isConstructor, checkSum }); return id; } public static Assembly[] InterestedAssemblies() { List required = new List(); required.Add("Spludlow"); foreach (DataRow row in Spludlow.Config.Applications().Tables[0].Rows) { if ((string)row["ApplicationType"] != "Lib") continue; string libDirectory = (string)row["Directory"]; string name = Path.GetFileName(libDirectory); required.Add(name); } List list = new List(); foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { string name = assembly.GetName().Name; if (required.Contains(name) == true) { list.Add(assembly); required.Remove(name); } } foreach (string name in required) { string assemblyPath = Spludlow.Config.ApplicationDirectory(name) + @"\" + name + ".dll"; if (File.Exists(assemblyPath) == false) { Spludlow.Log.Warning("Reflections InterestedAssemblies; Assembly not found: " + name + ", " + assemblyPath); } else { list.Add(Assembly.LoadFrom(assemblyPath)); } } return list.ToArray(); } public static string WriteTextFields(object instance) { StringBuilder text = new StringBuilder(); foreach (FieldInfo fieldInfo in instance.GetType().GetFields()) { object value = fieldInfo.GetValue(instance); if (value == null) continue; Type type = value.GetType(); if (Spludlow.SimpleEncoding.IsSimple(type) == false) continue; if (text.Length > 0) text.Append(", "); text.Append(fieldInfo.Name); text.Append(":"); text.Append(Spludlow.SimpleEncoding.Encode(value, type.Name)); } return text.ToString(); } public static void ReadTextFields(object instance, string text) { foreach (string fieldText in text.Split(new char[] { ',' })) { string[] fieldPair = fieldText.Split(new char[] { ':' }); string name = fieldPair[0].Trim(); string valueText = fieldPair[1].Trim(); if (valueText.Length == 0) continue; FieldInfo fieldInfo = instance.GetType().GetField(name); object value = Spludlow.SimpleEncoding.Decode(valueText, fieldInfo.FieldType.Name); fieldInfo.SetValue(instance, value); } } public static string[] MethodNames(Type type) { List names = new List(); MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); foreach (MethodInfo method in methods) { if (names.Contains(method.Name) == false) names.Add(method.Name); } return names.ToArray(); } } }