/// <summary> /// Gets the class that the method will operate in the context of. /// This is NOT necessarily the class that the method exists in. /// For extension methods the "this" parameter (first) will be returned /// regardless of whether it is a detour or not, pure static methods /// will return null (again, regardless of being a detour) as they /// don't operate in the context of class. Instance methods will /// return the defining class for non-detours and the class being /// injected into for detours. /// </summary> /// <returns>The method target class</returns> /// <param name="info">MethodInfo of the method to check</param> /// <param name="attribute">DetourMember attribute</param> private static Type GetMethodTargetClass(MethodInfo info, DetourMember attribute = null) { var methodType = GetMethodType(info); if (methodType == MethodType.Static) { // Pure static methods don't have a target class return(null); } if (methodType == MethodType.Extension) { // Regardless of whether this is the detour method or the method to be detoured, for extension methods we take the target class from the first parameter return(info.GetParameters()[0].ParameterType); } if (attribute == null) { info.TryGetAttribute(out attribute); } if (attribute != null) { if (attribute.targetClass != DetourMember.DefaultTargetClass) { return(attribute.targetClass); } return(info.DeclaringType.BaseType); } return(info.DeclaringType); }
/// <summary> /// Returns a list of detour methods with matching sequence and timing from a class. /// </summary> /// <param name="detours">List to add the detours from this class to</param> /// <param name="destinationType">The class to check for detour methods</param> /// <param name="sequence">Injection sequence</param> /// <param name="timing">Injection timing</param> private static void GetDetouredMethods(ref List <DetourPair> detours, Type destinationType) { var destinationMethods = destinationType .GetMethods(UniversalBindingFlags) .Where(destinationMethod => destinationMethod.HasAttribute <DetourMember>()) .ToList(); if (destinationMethods.NullOrEmpty()) { // No methods to detour return; } foreach (var destinationMethod in destinationMethods) { DetourMember attribute = null; if (destinationMethod.TryGetAttribute(out attribute)) { var memberClass = GetDetourTargetClass(destinationMethod, attribute); if (memberClass == null) { // Report and ignore any missing classes Log.Error(string.Format("MemberClass '{2}' resolved to null for '{0}.{1}'", destinationType.FullName, destinationMethod.Name, FullNameOfType(attribute.targetClass))); continue; } var sourceMethod = GetDetouredMethodInt(memberClass, attribute.targetMember, destinationMethod); if (sourceMethod == null) { // Report and ignore any missing methods Log.Error(string.Format("TargetMember '{2}.{3}' resolved to null for '{0}.{1}'", destinationType.FullName, destinationMethod.Name, memberClass.FullName, attribute.targetMember)); continue; } // Add detour for method detours.Add(new DetourPair(GetMethodTargetClass(sourceMethod, null), sourceMethod, GetMethodTargetClass(destinationMethod, attribute), destinationMethod)); } } }
/// <summary> /// Returns a list of detour property methods (get/set) with matching sequence and timing from a class. /// </summary> /// <param name="detours">List to add the detours from this class to</param> /// <param name="destinationType">The class to check for detour properties</param> /// <param name="sequence">Injection sequence</param> /// <param name="timing">Injection timing</param> private static void GetDetouredProperties(ref List <DetourPair> detours, Type destinationType) { var destinationProperties = destinationType .GetProperties(UniversalBindingFlags) .Where(destinationProperty => destinationProperty.HasAttribute <DetourMember>()) .ToList(); if (destinationProperties.NullOrEmpty()) { // No properties to detour return; } foreach (var destinationProperty in destinationProperties) { DetourMember attribute = null; if (destinationProperty.TryGetAttribute(out attribute)) { var memberClass = GetDetourTargetClass(destinationProperty, attribute); if (memberClass == null) { // Report and ignore any missing classes Log.Error(string.Format("MemberClass '{2}' resolved to null for '{0}.{1}'", destinationType.FullName, destinationProperty.Name, FullNameOfType(attribute.targetClass))); continue; } var sourceProperty = GetDetouredPropertyInt(memberClass, attribute.targetMember, destinationProperty); if (sourceProperty == null) { // Report and ignore any missing properties Log.Error(string.Format("TargetMember '{2}.{3}' resolved to null for '{0}.{1}'", destinationType.FullName, destinationProperty.Name, memberClass.FullName, attribute.targetMember)); continue; } var destinationMethod = destinationProperty.GetGetMethod(true); if (destinationMethod != null) { // Check for get method detour var sourceMethod = sourceProperty.GetGetMethod(true); if (sourceMethod == null) { // Report and ignore missing get method Log.Error(string.Format("TargetMember '{2}.{3}' has no get method for '{0}.{1}'", destinationType.FullName, destinationProperty.Name, memberClass.FullName, attribute.targetMember)); } else { // Add detour for get method detours.Add(new DetourPair(GetMethodTargetClass(sourceMethod, null), sourceMethod, GetMethodTargetClass(destinationMethod, attribute), destinationMethod)); } } destinationMethod = destinationProperty.GetSetMethod(true); if (destinationMethod != null) { // Check for set method detour var sourceMethod = sourceProperty.GetSetMethod(true); if (sourceMethod == null) { // Report and ignore missing set method Log.Error(string.Format("TargetMember '{2}.{3}' has no set method for '{0}.{1}'", destinationType.FullName, destinationProperty.Name, memberClass.FullName, attribute.targetMember)); } else { // Add detour for set method detours.Add(new DetourPair(GetMethodTargetClass(sourceMethod, null), sourceMethod, GetMethodTargetClass(destinationMethod, attribute), destinationMethod)); } } } } }
/// <summary> /// Gets the class that a detour will be injected into. /// </summary> /// <returns>The detour target class</returns> /// <param name="info">MemberInfo of the member to check</param> /// <param name="attribute">DetourMember attribute</param> private static Type GetDetourTargetClass(MemberInfo info, DetourMember attribute) { if (attribute != null) { if (attribute.targetClass != DetourMember.DefaultTargetClass) { return(attribute.targetClass); } var methodInfo = info as MethodInfo; if ( (info.DeclaringType.BaseType == typeof(System.Object)) && (methodInfo != null) && (GetMethodType(methodInfo) == MethodType.Extension) ) { return(methodInfo.GetParameters()[0].ParameterType); } return(info.DeclaringType.BaseType); } return(info.DeclaringType); }