public Detour(MethodBase method, IntPtr to) : this(method, DetourManager.GenerateNativeProxy(to, method)) { }
public Hook(MethodBase method, IntPtr to) : this(method, DetourManager.GenerateNativeProxy(to, method), null) { }
/// <summary> /// Generate a new DynamicMethod with which you can invoke the previous state. /// If the NativeDetour holds a reference to a managed method, a copy of the original method is returned. /// If the NativeDetour holds a reference to a native function, an "undo-call-redo" trampoline with a matching signature is returned. /// </summary> public MethodBase GenerateTrampoline(MethodBase signature = null) { if (_IsFree) { throw new InvalidOperationException("Free() has been called on this detour."); } if (_BackupMethod != null) { // If we're detouring an IL method and have an IL copy, invoke the IL copy. // Note that this ignores the passed signature. return(_BackupMethod); } if (signature == null) { signature = _BackupMethod; } if (signature == null) { throw new ArgumentNullException("A signature must be given if the NativeDetour doesn't hold a reference to a managed method."); } // Otherwise, undo the detour, call the method and reapply the detour. MethodBase methodCallable = Method; if (methodCallable == null) { methodCallable = DetourManager.GenerateNativeProxy(Data.Method, signature); } Type returnType = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] args = signature.GetParameters(); Type[] argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } DynamicMethod dm; string name = $"trampoline_native_{Method?.Name.ToString() ?? ((long) Data.Method).ToString("X16")}_{GetHashCode()}"; if (Method != null) { dm = new DynamicMethod( name, returnType, argTypes, Method.DeclaringType, true ); } else { dm = new DynamicMethod( name, returnType, argTypes, true ); } ILGenerator il = dm.GetILGenerator(); il.EmitDetourCopy(_BackupNative, Data.Method, Data.Size); // Store the return value in a local as we can't preserve the stack across exception block boundaries. LocalBuilder localResult = null; if (returnType != typeof(void)) { localResult = il.DeclareLocal(returnType); } Label blockTry = il.BeginExceptionBlock(); // TODO: Use specialized Ldarg.* if possible; What about ref types? for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } if (methodCallable is MethodInfo) { il.Emit(OpCodes.Call, (MethodInfo)methodCallable); } else if (methodCallable is ConstructorInfo) { il.Emit(OpCodes.Call, (ConstructorInfo)methodCallable); } else { throw new NotSupportedException($"Method type {methodCallable.GetType().FullName} not supported."); } if (localResult != null) { il.Emit(OpCodes.Stloc_0); } il.BeginFinallyBlock(); // Reapply the detour even if the method threw an exception. il.EmitDetourApply(Data); il.EndExceptionBlock(); if (localResult != null) { il.Emit(OpCodes.Ldloc_0); } il.Emit(OpCodes.Ret); return(dm.Pin()); }