예제 #1
0
 public Hook(Expression <Action> from, Delegate to, HookConfig config)
     : this(from.Body, to, ref config)
 {
 }
예제 #2
0
        public Hook(MethodBase from, MethodInfo to, object target, ref HookConfig config)
        {
            Method         = from;
            Target         = to;
            DelegateTarget = target;

            // Check if hook ret -> method ret is valid. Don't check for method ret -> hook ret, as that's too strict.
            Type returnType = (from as MethodInfo)?.ReturnType ?? typeof(void);

            if (to.ReturnType != returnType && !to.ReturnType.IsCompatible(returnType))
            {
                throw new InvalidOperationException($"Return type of hook for {from} doesn't match, must be {((from as MethodInfo)?.ReturnType ?? typeof(void)).FullName}");
            }

            if (target == null && !to.IsStatic)
            {
                throw new InvalidOperationException($"Hook for method {from} must be static, or you must pass a target instance.");
            }

            ParameterInfo[] hookArgs = Target.GetParameters();

            // Check if the parameters match.
            // If the delegate has got an extra first parameter that itself is a delegate, it's the orig trampoline passthrough.
            ParameterInfo[] args = Method.GetParameters();
            Type[]          argTypes;
            if (!Method.IsStatic)
            {
                argTypes    = new Type[args.Length + 1];
                argTypes[0] = Method.GetThisParamType();
                for (int i = 0; i < args.Length; i++)
                {
                    argTypes[i + 1] = args[i].ParameterType;
                }
            }
            else
            {
                argTypes = new Type[args.Length];
                for (int i = 0; i < args.Length; i++)
                {
                    argTypes[i] = args[i].ParameterType;
                }
            }

            Type origType = null;

            if (hookArgs.Length == argTypes.Length + 1 && typeof(Delegate).IsAssignableFrom(hookArgs[0].ParameterType))
            {
                _OrigDelegateType = origType = hookArgs[0].ParameterType;
            }
            else if (hookArgs.Length != argTypes.Length)
            {
                throw new InvalidOperationException($"Parameter count of hook for {from} doesn't match, must be {argTypes.Length}");
            }

            for (int i = 0; i < argTypes.Length; i++)
            {
                Type argMethod = argTypes[i];
                Type argHook   = hookArgs[i + (origType == null ? 0 : 1)].ParameterType;
                if (!argMethod.IsCompatible(argHook))
                {
                    throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related");
                }
            }

            MethodInfo origInvoke = _OrigDelegateInvoke = origType?.GetMethod("Invoke");
            // TODO: Check origType Invoke arguments.

            DynamicMethodDefinition dmd;
            ILProcessor             il;

            using (dmd = new DynamicMethodDefinition(
                       $"Hook<{Method.GetID(simple: true)}>?{GetHashCode()}",
                       (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes
                       )) {
                il = dmd.GetILProcessor();

                if (target != null)
                {
                    _RefTarget = il.EmitReference(target);
                }

                if (origType != null)
                {
                    _RefTrampoline = il.EmitReference <Delegate>(null);
                }

                for (int i = 0; i < argTypes.Length; i++)
                {
                    il.Emit(OpCodes.Ldarg, i);
                }

                il.Emit(OpCodes.Call, Target);

                il.Emit(OpCodes.Ret);

                TargetReal = dmd.Generate().Pin();
            }

            // Temporarily provide a trampoline that waits for the proper trampoline.
            if (origType != null)
            {
                ParameterInfo[] origArgs     = origInvoke.GetParameters();
                Type[]          origArgTypes = new Type[origArgs.Length];
                for (int i = 0; i < origArgs.Length; i++)
                {
                    origArgTypes[i] = origArgs[i].ParameterType;
                }

                using (dmd = new DynamicMethodDefinition(
                           $"Chain:TMP<{Method.GetID(simple: true)}>?{GetHashCode()}",
                           (origInvoke as MethodInfo)?.ReturnType ?? typeof(void), origArgTypes
                           )) {
                    il = dmd.GetILProcessor();

                    // while (ref == null) { }
                    _RefTrampolineTmp = il.EmitReference <Delegate>(null);
                    il.Emit(OpCodes.Brfalse, il.Body.Instructions[0]);

                    // Invoke the generated delegate.
                    il.EmitGetReference <Delegate>(_RefTrampolineTmp.Value);

                    for (int i = 0; i < argTypes.Length; i++)
                    {
                        il.Emit(OpCodes.Ldarg, i);
                    }

                    il.Emit(OpCodes.Callvirt, origInvoke);

                    il.Emit(OpCodes.Ret);

                    DynamicMethodHelper.SetReference(_RefTrampoline.Value, dmd.Generate().CreateDelegate(origType));
                }
            }

            _Detour = new Detour(Method, TargetReal, new DetourConfig()
            {
                ManualApply = config.ManualApply,
                Priority    = config.Priority,
                ID          = config.ID,
                Before      = config.Before,
                After       = config.After
            });

            _UpdateOrig(null);
        }
예제 #3
0
 public Hook(Expression from, Delegate to, HookConfig config)
     : this(((MethodCallExpression)from).Method, to, ref config)
 {
 }
예제 #4
0
 public Hook(Expression <Action> from, IntPtr to, HookConfig config)
     : this(from.Body, to, ref config)
 {
 }
예제 #5
0
 public Hook(Delegate from, Delegate to, HookConfig config)
     : this(from.Method, to, ref config)
 {
 }
예제 #6
0
 public Hook(Expression from, IntPtr to, ref HookConfig config)
     : this(((MethodCallExpression)from).Method, to, ref config)
 {
 }
예제 #7
0
 public Hook(Delegate from, IntPtr to, ref HookConfig config)
     : this(from.Method, to, ref config)
 {
 }
예제 #8
0
 public Hook(MethodBase method, Delegate to, HookConfig config)
     : this(method, to.Method, to.Target, ref config)
 {
 }
예제 #9
0
 public Hook(MethodBase method, IntPtr to, HookConfig config)
     : this(method, DetourHelper.GenerateNativeProxy(to, method), null, ref config)
 {
 }
예제 #10
0
 public Hook(MethodBase from, MethodInfo to, HookConfig config)
     : this(from, to, null, ref config)
 {
 }
예제 #11
0
 public Hook(MethodBase from, MethodInfo to, object target, HookConfig config)
     : this(from, to, target, ref config)
 {
 }