Monday, January 25, 2010

.NET hacking made easy

I am in a mood this year so I decided to spend some time with a problem that I have left in the past.

The problem

I really wanted to use Runtime member for existent Types and also enable validation for those members. But for the reason i have describe in this post it was not possible, unless I change DevExpress source code and recompile (bad choice). DevExpress and any vendor could, for their own reasons not support your suggestion. Maybe for good or for bad I really not care, what I care is how to use Runtime member for existent Types with validation enable.

So I will try a different approach I will use CThru which is an AOP open source interception framework based on the Typemock Open-AOP API to “For any (past or future) object of type X in the system, make property Y of said object already return a fake value” and in my case that will be

For any (past or future) object of type RulePropertyValueProperties, make property TargetPropertyName return my version of code

Lets say for a moment that you know nothing on CThru, and even the idea of learning a “strange AOP” frameworks frightens you cause of the luck of time. I ll explore the proccess of doing the above like I know nothing about it also

Previous post of mine about AOP can be found here

1st Step

Go to their site at codeplex http://cthru.codeplex.com/ and read all stuff under Learn section. There are not many info there but very informative as you have already notice if you follow the above links I posted.

2nd Step

Download the source code and… here it is—> a CThru.Tests project. That will do all the job!!! There should be tests explaining how the framework works. After exploring the solution I came across TraceAspect class, which was mention to me at CThru site. That class/aspect can enable logging to any class within .Net framework except for classes within mscorlib. Just take a look how is is to implement it

    public class TraceAspect : Aspect {

        private readonly Predicate<InterceptInfo> _shouldIntercept;

        private readonly string _logPath;

 

        public TraceAspect(Predicate<InterceptInfo> shouldIntercept, string logPath) {

            _shouldIntercept = shouldIntercept;

            _logPath = logPath;

        }

 

        public TraceAspect (Predicate<InterceptInfo> shouldIntercept) {

            _shouldIntercept = shouldIntercept;

        }

 

        public override bool ShouldIntercept (InterceptInfo info) {

            return _shouldIntercept(info);

        }

 

        public override void ConstructorBehavior (DuringCallbackEventArgs e) {

            DoTrace(e);

        }

 

        public override void MethodBehavior (DuringCallbackEventArgs e) {

            DoTrace(e);

        }

 

        protected virtual void DoTrace (DuringCallbackEventArgs e) {

            string message = GetMessage(e);

            DoTrace(message);

        }

 

        protected virtual void DoTrace(string message) {

            if (_logPath != null) System.IO.File.AppendAllText(_logPath, message + Environment.NewLine);

            Console.WriteLine(message);

        }

 

        protected virtual string GetMessage(DuringCallbackEventArgs e) {

            string message;

            try {

                string parameters = GetParameters(e);

                message = string.Concat(e.TypeName, ".", e.MethodName, "(", parameters, ")");

            }

            catch (Exception ex) {

                message = ex.ToString() + Environment.NewLine;

            }

            return message;

        }

 

        protected virtual string GetParameters (DuringCallbackEventArgs e) {

            var list = new List<string>();

            if (e.ParameterValues != null) {

                foreach (var param in e.ParameterValues) {

                    var value = param != null ? param.ToString() : "null";

                    list.Add(value);

                }

            }

            return string.Join(", ", list.ToArray());

        }

    }

Just inheriting from CThru.Aspect abstract class and implement ShouldIntercept abstract method to return a Predicate<InterceptInfo> delegate you are able to log any type you want with a very simple call like

var aspect = new TraceAspect(info => info.TargetInstance is IHttpHandler, @"C:\log.txt");

Very very powerfull and easy!!! many thnks for that wonderful API to Roy Osherove and TypeMock people.

So back to our case our aspect will look like

    public class ExistentMembersEnableValidationAspect : Aspect {

        static FieldInfo backingField;

 

 

        public ExistentMembersEnableValidationAspect() {

            backingField =

                typeof (RulePropertyValueProperties).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(

                    info => info.Name == "propertyName").Single();

        }

 

        public override bool ShouldIntercept(InterceptInfo info) {

            return info.TargetInstance is RulePropertyValueProperties;

        }

 

        public override void MethodBehavior(DuringCallbackEventArgs e) {

            base.MethodBehavior(e);

            if (e.MethodName == "set_TargetPropertyName") {

                e.MethodBehavior = MethodBehaviors.SkipActualMethod;

                backingField.SetValue(e.TargetInstance, e.ParameterValues[0]);

            }

        }

    }

and the spec I wrote using MSpec is like

image

I am sure you can see why “hacking” is on my post title by now :) the above approach could be used to bypass a licensing system as well

To finalize this long post, I understand that some of you may not like that approach again for your own reasons that I would be happy to learn. So I am going to create a new platform suffix for eXpand modules the CThru platform suffix.

In order to use the above solution you have to register the eXpand.ExpressApp.WorldCreator.CThru assembly found in eXpand.DLL folder

But to those of you that like and want to use the above approach I need to remind you that eXpand has an open source lisence for CThru and TypeMock so please feel free to contact me so you can get your lisence to be used with eXpand

You may also wondering about speed implications. The following controller will fix that for you

public partial class RuleSetInitializationController : DevExpress.ExpressApp.Validation.RuleSetInitializationController

{

    public RuleSetInitializationController()

    {

        InitializeComponent();

        RegisterActions(components);

    }

    protected override void OnActivated()

    {

        if (Application != null) {

            var module = (ValidationModule) Application.Modules.FindModule(typeof (ValidationModule));

            if (module != null) {

                CThruEngine.AddAspect(new ExistentMembersEnableValidationAspect());

                CThruEngine.StartListening();

                InternalMockManager.Locked = false;

                module.RuleSetInitialized += (sender, args) => {

                    CThruEngine.StopListeningAndReset();

                    InternalMockManager.Locked = true;

                };

                module.InitializeRuleSet();

            }

            else {

                Tracing.Tracer.LogWarning("Cannot find Validation module in Module list: {0}",typeof (ValidationModule).AssemblyQualifiedName);

            }

        }

    }

 

}

since it will create the aspect only when its needed. And to prove my saying watch the next video

Subscribe to XAF feed
Subscribe to community feed

DiggIt!
blog comments powered by Disqus