zoukankan      html  css  js  c++  java
  • Aop RealProxy 千年遇BUG

    昨夜一名CYQ.Data 框架爱好者在测试V5Beta版本时,向我发一个问题, 说F5下正常,直接运行exe会报错,于是,一夜深究后,就有了今天的博文。

     

    当你运行一段代码、一个软件,在运行时你发现:Debug下正常, Release下F5运行也正常,唯独Release编绎后单独运行异常,你会怎么想?

     

    这个问题对我而言,脑海里不曾有过,于是蛋定思蛋,先百度了一下:发现网上信息并不多,偶尔几条也是VC++的,对于C#的,绝无仅无。

     

    当我把代码发给一友人,让他也帮忙试下时,发现对方的win7 64位竟然运行正常,灵异性又提升了一个等级。

     

    面对这神一般的灵异Bug,我费了N个小时折腾,找一个不能调试的Bug,因为F5下都是正常的,那个辛苦啊。

    灵异现象已经说了,那灵异点是什么呢?标题已经出卖了这个问题,和Aop的RealProxy有关。

    光说AOP,园子里懂的人不多,用的估计也不多,所以深入无谓,浅出又难,只好简单过过场。 

    我是怎么发现问题是在Aop RealProxy?

    这个使用常规方法,在代码段里插一些弹出信息,来缩小出错的代码片断,最终找到了它。

    为了减少废话,这里直接讲述两点:

    1:哪些代码有问题,哪里有问题?

    以前我写过一篇文章:C# Aop简单扫盲及ORM实体类属性拦截示例,可以先理解下AOP,但是它是有问题的,就是今天的问题。

    这里给出详细的错误代码,和注释错误点,代码通常都有点长,不容易看:

    View Code 
       
        using System;
        using System.Reflection;
        using System.Runtime.Remoting.Activation;
        using System.Runtime.Remoting.Messaging;
        using System.Runtime.Remoting.Proxies;

        internal class AopAttribute : ProxyAttribute
        {
            public override MarshalByRefObject CreateInstance(Type serverType)
            {
                AopProxy proxy = new AopProxy(serverType);
                return (proxy.GetTransparentProxy() as MarshalByRefObject);
            }
        }
        internal class AopProxy : RealProxy
        {
            
            public AopProxy(Type serverType) : base(serverType)
            {
                
            }

            public override IMessage Invoke(IMessage msg)
            {
                IMessage message3;
                if (msg == null)
                {
                    return msg;
                }
                if (msg is IConstructionCallMessage)
                {
                    IConstructionReturnMessage message = base.InitializeServerObject((IConstructionCallMessage) msg);
                    RealProxy.SetStubData(this, message.ReturnValue);
                    return message;
                }
                if (!(msg is IMethodCallMessage))
                {
                    return msg;
                }
                IMethodCallMessage mcm = msg as IMethodCallMessage;
                try
                {
                    object[] args = mcm.Args;
                    message3 = new ReturnMessage(mcm.MethodBase.Invoke(base.GetUnwrappedServer(), args), args, args.Length, mcm.LogicalCallContext, mcm);
                }
                catch (Exception exception)
                {
                    throw new Exception(exception.Message);
                }
                return message3;
            }

          
        }
        [AopAttribute]
        public class Users : ContextBoundObject
        {
            public Users()
            {

            }
            int i = 0;//任何的成员变量,在被Aop调用的时候,都会报“未将对象引用到对象实例的错误”
            public void Test()
            {
                try
                {
                    i = 1;
                }
                catch (Exception err)
                {
                    System.Windows.Forms.MessageBox.Show(err.Message);
                }
            }

    通常大量的代码测试及分析方法,我发现了,只要Aop对象涉及到内部成员变量,在Release编绎后运行就一定会报错。

    2:如何解决。 

    在历经各种无解后,我发现了,RealProxy还有另一种写法,而这另一种写法,竟然可以解决这个问题,代码如下:

    View Code 
     
    class AopAttribute : ProxyAttribute
        {

            public override MarshalByRefObject CreateInstance(Type serverType)
            {
                AopProxy realProxy = new AopProxy(serverType, base.CreateInstance(serverType));
                return realProxy.GetTransparentProxy() as MarshalByRefObject;

            }
        }
        class AopProxy : RealProxy
        {
            MethodInfo method;
            MarshalByRefObject _target = null;
            public AopProxy(Type serverType,MarshalByRefObject target)
                : base(serverType)
            {
                _target = target;
                method = serverType.GetMethod("Set", BindingFlags.NonPublic | BindingFlags.Instance);
            }
            public override IMessage Invoke(IMessage msg)
            {
                if (msg != null)
                {
                    if (msg is IConstructionCallMessage)
                    {
                        IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage;
                      
                        RealProxy defaultProxy = RemotingServices.GetRealProxy(_target);

                        //如果不做下面这一步,_target还是一个没有直正实例化被代理对象的透明代理,
                        
    //这样的话,会导致没有直正构建对象。
                        defaultProxy.InitializeServerObject(constructCallMsg);

                        //本类是一个RealProxy,它可通过GetTransparentProxy函数得到透明代理
                        return System.Runtime.Remoting.Services.EnterpriseServicesHelper.CreateConstructionReturnMessage(constructCallMsg, (MarshalByRefObject)GetTransparentProxy());

                    }
                    else if (msg is IMethodCallMessage)
                    {
                        IMethodCallMessage callMsg = msg as IMethodCallMessage;
                        object[] args = callMsg.Args;

                        //System.Windows.Forms.MessageBox.Show(callMsg.MethodBase.ToString());

                        if (callMsg.MethodName.StartsWith("set_") && args.Length == 1)
                        {
                            method.Invoke(_target, new object[] { callMsg.MethodName.Substring(4), args[0] });//对属性进行调用
                        }
                        return RemotingServices.ExecuteMessage(_target, callMsg);
                    }
                }
                return msg;
            }

    3:Debug和Release还有F5,究竟隐藏怎样的秘密? 

    我也不懂,这涉及更底层的问题,不在我深究的范围内,懂的人士可以留言给大伙解答下。

    总结:

    在编程的世界里,灵异总是无处不在的,但是找它灵异的原因,并且能找到一种解决这类事件的方法,是每个代码建造师必须具有能力。
    至于为何会产生这样那样的灵异事件,在研究的范围内,可以追究原因,在非自身研究的领域,大伙看着办了。 

    终于,在忙碌微博精灵系列软件的日子里,终于又抽空完成了一篇文章,感觉不容易啊。

  • 相关阅读:
    Ado.Net 调用存储过程用法
    C程序设计语言练习题1-22
    C程序设计语言练习题1-21
    C程序设计语言练习题1-20
    C程序设计语言练习题1-19
    C程序设计语言练习题1-18
    C程序设计语言练习题1-17
    C程序设计语言练习题1-16
    C程序设计语言练习题1-15
    C程序设计语言练习题1-14
  • 原文地址:https://www.cnblogs.com/cyq1162/p/2662492.html
Copyright © 2011-2022 走看看