.Net中的Interception--一个简单的AOP框架学习

最近翻手头的dll文件时无意中发现了一个Interception实现,这个框架比起目前流行的AOP框架显的比较简漏,但却很好的体现出了.net下AOP是怎么实现的,于是就整理出来。

在.Net Unity2.0中的Interception,按三种方式实现:

1.TransparentProxy/RealProxy Interceptor 即Remoting代理机制。

2.Interface Interceptor  即动态代码 (Emit编程)实现

3.Virtual Method Interceptor 也是动态代码实现,Emit编程基本类似于IL编程了。

需要注意的是使用VirtuatMethodInterceptor后,PolicyInjectionBehavior会被忽略,通过Resovle获取的始终是继承被拦截类的子类实例
而透明代理与接口方式在全部移除匹配策略后(Policy,就是这个类不需要拦截了),Resolve返回的是原始类(非代理类)

 

 

实例拦截与类型拦截

1.实例拦截

 

TransparentProxy 与 Interface Interceptor 属于实例拦截,所谓实例拦截就是被拦截对象完整而独立的在内参中存在。Client端通过代理类与被拦截对象发生通信(方法调用)。

2.类型拦截

Virtual Method 方式属于类型拦截,内参中不存在被拦截类型的实例,拦截框架通过动态代码生成被拦截类型的子类型程序集,该程序集被加载后对应的子类型被实例化于内存中与Client发生通信

 

下面针对TransparentProxy/RealProxy 与 Interface Interceptor帖出手头上dll实现的代码

1.TransparentProxy/RealProxy实现

    using System.Reflection;
    using System.Runtime.Remoting.Proxies;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Remoting;


    internal class TransparentProxy : RealProxy
    {
        // Fields
        private readonly MarshalByRefObject m_Target;

        // Methods
        public TransparentProxy(MarshalByRefObject target)
            : base(target.GetType())
        {
            this.m_Target = target;
        }

        public static object GetProxy(MarshalByRefObject target)
        {
            TransparentProxy proxy = new TransparentProxy(target);
            return proxy.GetTransparentProxy();
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMessage message = null;
            IMethodCallMessage callMsg = msg as IMethodCallMessage;
            if (callMsg != null)
            {
                object[] customAttributes = callMsg.MethodBase.GetCustomAttributes(true);
                this.InvokeBeforeAttribute(customAttributes, callMsg);
                try
                {
                    message = RemotingServices.ExecuteMessage(this.m_Target, callMsg);
                }
                catch (Exception exception)
                {
                    this.InvokeExceptionAttribute(customAttributes, callMsg, exception);
                    throw;
                }
                this.InvokeAfterAttribute(customAttributes, callMsg, ((ReturnMessage)message).ReturnValue);
            }
            return message;
        }
      //..........
      //............
    }

类TransproxyProxy中维护着一个到target类实例的引用(必须是MarshalByRefObject类型的子类),最终的方法调用会通过消息机制到达target实例--语句RemotingServices.ExecuteMessage(this.m_Target, callMsg);,在调用目标对象的目标方法之前会调用InvokeBeforeAttribute,错误时会调用InvokeExceptionAttribute,而完成后调用InvokeAfterAttribute.这里需要注意的是Unity2.0 Interception 中将要调用的InterceptionBehavior构建成管道模型,编程时会有Scop样的开闭结构,而这里的实现只是顺序的调用,这点需要加以区分。InvokeBeforeAttribute等方法实现如下

        private void InvokeAfterAttribute(object[] attributes, IMethodCallMessage callMsg, object result)
        {
            foreach (object obj2 in attributes)
            {
                AfterAttribute attribute = obj2 as AfterAttribute;
                if (attribute != null)
                {
                    attribute.Invoke(this.m_Target, callMsg.MethodBase, callMsg.InArgs, result);
                }
            }
            List<IInterception> interceptionList = ProxyBuilder.GetInterceptionList(this.m_Target.GetType().FullName + "." + callMsg.MethodName, InterceptionType.After);
            if (interceptionList != null)
            {
                foreach (IInterception interception in interceptionList)
                {
                    interception.Invoke(this.m_Target, callMsg.MethodBase, callMsg.InArgs, result);
                }
            }
        }

2.Interface Interceptor 代码

     public static object GetProxyInstance(object target, Type interfaceType)
        {
            return Activator.CreateInstance(GetProxyType(target.GetType(), interfaceType), new object[] { target, interfaceType });
        }

        private static Type GetProxyType(Type targetType, Type interfaceType)
        {
            AppDomain domain = Thread.GetDomain();
            AssemblyName name = new AssemblyName();
            name.Name = "TempAssemblyInjection";
            AssemblyName name2 = name;
            AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(name2, AssemblyBuilderAccess.Run);
            ModuleBuilder builder = assemblyBuilder.DefineDynamicModule("TempClassInjection");
            Type type = builder.GetType("TempAssemblyInjection__Proxy" + interfaceType.Name + targetType.Name);
            if (type != null)
            {
                return type;
            }
            m_TypeBuilder = builder.DefineType("TempAssemblyInjection__Proxy" + interfaceType.Name + targetType.Name, TypeAttributes.Public, targetType.BaseType, new Type[] { interfaceType });
            m_Target = m_TypeBuilder.DefineField("target", interfaceType, FieldAttributes.Private);
            m_Interface = m_TypeBuilder.DefineField("iface", typeof(Type), FieldAttributes.Private);
            CreateConstructor(m_TypeBuilder, m_Target, m_Interface);
            foreach (MethodInfo info in interfaceType.GetMethods())
            {
                CreateProxyMethod(info, m_TypeBuilder);
            }
            
            return m_TypeBuilder.CreateType();
            
        }

 上面代码通过Emit编程动态构建程序集,程序集中包括一个到目标类的代理类,针对给定接口中的方法签名逐个创建代理方法--语句CreateProxyMethod(info, m_TypeBuilder);
另外可以看到代理类型的程序集只在第一次访问时被创建
--语句 
 Type type = builder.GetType("TempAssemblyInjection__Proxy" + interfaceType.Name + targetType.Name);
   if (type != null)
    {
         return type;
     }

建立的代理类定义类似如下代码:

public class TempAssemblyInjection__ProxyIAnimalDog : IAnimal
{
    // Fields
    private Type iface;
    private IAnimal target;

    // Methods
    public TempAssemblyInjection__ProxyIAnimalDog(object obj1, Type type1)
    {
        this.target = (IAnimal) obj1;
        this.iface = type1;
    }

    public override int Run(int num1, int num2)
    {
        object[] parameters = new object[] { num1, num2 };
        return (int) DynamicProxy.InterceptHandler(this.target, 
Helper.GetMethodFromType(this.target.GetType(), MethodBase.GetCurrentMethod()),
parameters,
Helper.AspectUnion(Helper.GetMethodFromType(this.iface, MethodBase.GetCurrentMethod()).GetCustomAttributes(typeof(AspectAttribute), true))
); } }


 DynamicProxy.InterceptHandler的代码

        public static object InterceptHandlerMethod(object target, MethodBase method, object[] parameters, AspectAttribute[] attributes)
        {
            object obj2;
            foreach (AspectAttribute attribute in attributes)
            {
                if (attribute is BeforeAttribute)
                {
                    attribute.Invoke(target, method, parameters, null);
                }
            }
            foreach (IInterception interception in ProxyBuilder.GetInterceptionList(target.GetType().FullName + "." + method.Name, InterceptionType.Before))
            {
                interception.Invoke(target, method, parameters, null);
            }
            try
            {
                obj2 = target.GetType().GetMethod(method.Name).Invoke(target, parameters);
            }
            catch (Exception exception)
            {
                foreach (AspectAttribute attribute2 in attributes)
                {
                    if (attribute2 is ExceptionAttribute)
                    {
                        attribute2.Invoke(target, method, parameters, exception);
                    }
                }
                foreach (IInterception interception2 in ProxyBuilder.GetInterceptionList(target.GetType().FullName + "." + method.Name, InterceptionType.Exception))
                {
                    interception2.Invoke(target, method, parameters, exception);
                }
                throw;
            }
            foreach (AspectAttribute attribute3 in attributes)
            {
                if (attribute3 is AfterAttribute)
                {
                    attribute3.Invoke(target, method, parameters, obj2);
                }
            }
            foreach (IInterception interception3 in ProxyBuilder.GetInterceptionList(target.GetType().FullName + "." + method.Name, InterceptionType.After))
            {
                interception3.Invoke(target, method, parameters, obj2);
            }
            return obj2;
        }

        // Properties
        public static Callback InterceptHandler
        {
            get
            {
                return new Callback(DynamicProxy.InterceptHandlerMethod);
            }
        }

完成代码与使用Demo请下载演示包

==================================点这里下载=========================

模拟鼠标和键盘 注意:不支持Windows 8 / 8.1。 Interceptor是Windows键盘驱动程序的包装器(包装http://oblita.com/Interception)。 使用驱动程序,Interceptor可以模拟按键和鼠标点击... 使用DirectX的游戏,通常不接受使用SendInput()的击键 Windows的受保护区域,如Windows登录屏幕或UAC调暗屏幕 任何应用程序 因为驱动程序模拟击键和鼠标单击,所以目标窗口必须处于活动状态(即,在发送击键和鼠标点击时,不能在另一个窗口上执行多任务)。 如何使用 下载并构建此项目并在项目引用其DLL。 下载'interception.dll',这是一个由驱动程序作者编写的独立库。将它放在与可执行文件相同的目录。这是必需的。 从作者的网页下载并安装“install-interception.exe”。安装后重新启动计算机。 在您的代码,要加载驱动程序,请调用(阅读下面的代码注释;您必须设置过滤模式以捕获按键事件或发送按键操作!): Input input = new Input(); // Be sure to set your keyboard filter to be able to capture key presses and simulate key presses // KeyboardFilterMode.All captures all events; 'Down' only captures presses for non-special keys; 'Up' only captures releases for non-special keys; 'E0' and 'E1' capture presses/releases for special keys input.KeyboardFilterMode = KeyboardFilterMode.All; // You can set a MouseFilterMode as well, but you don't need to set a MouseFilterMode to simulate mouse clicks // Finally, load the driver input.Load(); 做你的东西。 input.MoveMouseTo(5, 5); // Please note this doesn't use the driver to move the mouse; it uses System.Windows.Forms.Cursor.Position input.MoveMouseBy(25, 25); // Same as above ^ input.SendLeftClick(); input.KeyDelay = 1; // See below for explanation; not necessary in non-game apps input.SendKeys(Keys.Enter); // Presses the ENTER key down and then up (this constitutes a key press) // Or you can do the same thing above using these two lines of code input.SendKeys(Keys.Enter, KeyState.Down); Thread.Sleep(1); // For use in games, be sure to sleep the thread so the game can capture all events. A lagging game cannot process input quickly, and you so you may have to adjust this to as much as 40 millisecond delay. Outside of a game, a delay of even 0 milliseconds can work (instant key presses). input.SendKeys(Keys.Enter, KeyState.Up); input.SendText("hello, I am typing!"); /* All these following characters / numbers / symbols work */ input.SendText("abcdefghijklmnopqrstuvwxyz"); input.SendText("1234567890"); input.SendText("!@#$%^&*()"); input.SendText("[]\\;',./"); input.SendText("{}|:\"?"); // And finally input.Unload(); 笔记: BadImageFormatException如果您没有为解决方案的所有项目(包括此项目)使用正确的体系结构(x86或x64),则可能会获得。因此,您可能必须下载此项目的源代码才能将其重建为正确的体系结构。这应该很简单,构建过程应该没有错误。 您必须从http://oblita.com/Interception下载'interception.dll' 。 如果你已经完成了以上所有操作(正确安装了拦截驱动程序,将interception.dll放在你的项目文件夹),你仍然无法发送击键: 驱动程序有一个限制,即它不能在不接收至少一次击键的情况下发送击键。这是因为驱动程序不知道键盘是哪个设备ID,因此它必须等待接收击键以从击键推断出设备ID。 总之,在发送击键之前,请始终按键盘一次。点按任意键。然后你可以发送击键。这不适用于接收击键,因为通过接收击键,您当然已经按下了一个键。 MoveMouseTo()和MoveMouseBy()完全忽略键盘驱动程序。它使用System.Windows.Forms.Position来设置和获取游标的位置(它为下面的各个函数调用标准的Win32 API)。 原因是,在探索键盘驱动程序的鼠标移动功能时,我注意到它没有按像素单位移动光标,而是似乎通过加速移动光标。当我想将光标移动到某个位置时,这会不断产生不一致的值。因为Win32游标设置API通常不被游戏等阻止,所以我发现只需调用这些标准API即可,而无需使用驱动程序。请注意,这仅适用于设置光标位置。拦截光标仍然可以正常工作。例如,您可以使用Interceptor反转鼠标的x和y轴。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值