예제 #1
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);
        }
예제 #2
0
파일: Hook.cs 프로젝트: qipa/MonoMod
        public Hook(MethodBase from, MethodInfo to, object target)
        {
            Method = from;
            _Hook  = to;

            if (_Hook.ReturnType != ((from as MethodInfo)?.ReturnType ?? typeof(void)))
            {
                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 = _Hook.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.DeclaringType;
                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))
            {
                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.IsAssignableFrom(argHook) &&
                    !argHook.IsAssignableFrom(argMethod))
                {
                    throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related");
                }
            }

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

            DynamicMethod dm;
            ILGenerator   il;

            dm = new DynamicMethod(
                $"hook_{Method.Name}_{GetHashCode()}",
                (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes,
                Method.DeclaringType,
                true
                );
            il = dm.GetILGenerator();

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

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

            // TODO: Use specialized Ldarg.* if possible; What about ref types?
            for (int i = 0; i < argTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i);
            }

            il.Emit(OpCodes.Call, _Hook);

            il.Emit(OpCodes.Ret);

            Target = dm.Pin();

            _Detour = new Detour(Method, Target);

            if (origType != null)
            {
                DynamicMethodHelper.SetReference(_RefTrampoline.Value, GenerateTrampoline(origInvoke).CreateDelegate(origType));
            }
        }
예제 #3
0
파일: Hook.cs 프로젝트: awaescher/MonoMod
        public Hook(MethodBase from, MethodInfo to, object target)
        {
            Method = from;
            _Hook  = to;

            if (!(OnDetour?.InvokeWhileTrue(this, from, to, target) ?? true))
            {
                return;
            }

            // 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 (_Hook.ReturnType != returnType && !_Hook.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 = _Hook.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.

            DynamicMethod dm;
            ILGenerator   il;

            dm = new DynamicMethod(
                $"Hook<{Method}>?{GetHashCode()}",
                (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes,
                Method.DeclaringType,
                true
                );
            il = dm.GetILGenerator();

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

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

            // TODO: Use specialized Ldarg.* if possible; What about ref types?
            for (int i = 0; i < argTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i);
            }

            il.Emit(OpCodes.Call, _Hook);

            il.Emit(OpCodes.Ret);

            Target = dm.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;
                }

                dm = new DynamicMethod(
                    $"Chain:TMP<{Method}>?{GetHashCode()}",
                    (origInvoke as MethodInfo)?.ReturnType ?? typeof(void), origArgTypes,
                    Method.DeclaringType,
                    true
                    );
                il = dm.GetILGenerator();

                // while (ref == null) { }
                Label lblStart = il.DefineLabel();
                il.MarkLabel(lblStart);
                _RefTrampolineTmp = il.EmitReference <Delegate>(null);
                il.Emit(OpCodes.Brfalse, lblStart);

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

                // TODO: Use specialized Ldarg.* if possible; What about ref types?
                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, dm.CreateDelegate(origType));
            }

            _Detour = new Detour(Method, Target);

            UpdateOrig(null);
        }