/// <summary> /// Generates instructions to load arguments or default values onto the stack in a /// detour method. /// </summary> /// <param name="generator">The method where the calls will be added.</param> /// <param name="actualParams">The actual parameters required.</param> /// <param name="expectedParams">The parameters provided.</param> /// <param name="offset">The offset to start loading (0 = static, 1 = instance).</param> private static void LoadParameters(ILGenerator generator, ParameterInfo[] actualParams, Type[] expectedParams, int offset) { // Load the known method arguments onto the stack int n = expectedParams.Length, need = actualParams.Length + offset; if (n > 0) { generator.Emit(OpCodes.Ldarg_0); } if (n > 1) { generator.Emit(OpCodes.Ldarg_1); } if (n > 2) { generator.Emit(OpCodes.Ldarg_2); } if (n > 3) { generator.Emit(OpCodes.Ldarg_3); } for (int i = 4; i < n; i++) { generator.Emit(OpCodes.Ldarg_S, i); } // Load the rest as defaults for (int i = n; i < need; i++) { var param = actualParams[i - offset]; PTranspilerTools.GenerateDefaultLoad(generator, param.ParameterType, param. DefaultValue); } }
/// <summary> /// Creates a dynamic detour method of the specified delegate type to wrap a base game /// method with the specified name. The dynamic method will automatically adapt if /// optional parameters are added, filling in their default values. /// </summary> /// <typeparam name="D">The delegate type to be used to call the detour.</typeparam> /// <param name="target">The target method to be called.</param> /// <returns>The detour that will call that method.</returns> /// <exception cref="DetourException">If the delegate does not match the target.</exception> public static D Detour <D>(this MethodInfo target) where D : Delegate { if (target == null) { throw new ArgumentNullException("target"); } if (target.ContainsGenericParameters) { throw new ArgumentException("Generic types must have all parameters defined"); } var expected = DelegateInfo.Create(typeof(D)); var parentType = target.DeclaringType; var expectedParamTypes = expected.ParameterTypes; var actualParams = ValidateDelegate(expected, target); int offset = target.IsStatic ? 0 : 1; // Method will be "declared" in the type of the target, as we are detouring around // a method of that type var caller = new DynamicMethod(target.Name + "_Detour", expected.ReturnType, expectedParamTypes, parentType, true); var generator = caller.GetILGenerator(); // Load the known method arguments onto the stack int n = expectedParamTypes.Length, need = actualParams.Length + offset; if (n > 0) { generator.Emit(OpCodes.Ldarg_0); } if (n > 1) { generator.Emit(OpCodes.Ldarg_1); } if (n > 2) { generator.Emit(OpCodes.Ldarg_2); } if (n > 3) { generator.Emit(OpCodes.Ldarg_3); } for (int i = 4; i < n; i++) { generator.Emit(OpCodes.Ldarg_S, i); } // Load the rest as defaults for (int i = n; i < need; i++) { var param = actualParams[i - offset]; PTranspilerTools.GenerateDefaultLoad(generator, param.ParameterType, param. DefaultValue); } if (parentType.IsValueType || target.IsStatic) { generator.Emit(OpCodes.Call, target); } else { generator.Emit(OpCodes.Callvirt, target); } generator.Emit(OpCodes.Ret); // Define the parameter names, parameter indexes start at 1 if (offset > 0) { caller.DefineParameter(1, ParameterAttributes.None, "this"); } for (int i = offset; i < n; i++) { var oldParam = actualParams[i - offset]; caller.DefineParameter(i + 1, oldParam.Attributes, oldParam.Name); } #if DEBUG PUtil.LogDebug("Created delegate {0} for method {1}.{2} with parameters [{3}]". F(caller.Name, parentType.FullName, target.Name, actualParams.Join(","))); #endif return(caller.CreateDelegate(typeof(D)) as D); }