Sometimes we need to have access to non-public members. However, if you try to do this via reflection in Silverlight, you get something like this:
Attempt by method 'method name' to access method 'member name' failed.
Using dynamic method and emit
The first thing I tried was the DynamicMethod. It had a constructor with parameter restrictedSkipVisibility, here's a description:- restrictedSkipVisibility - true to skip JIT visibility checks on types and members accessed by the MSIL of the dynamic method; otherwise, false.
Attempt by security transparent method 'method name' to access security critical method 'System.Reflection.Emit.DynamicMethod..ctor(System.String, System.Type, System.Type[], System.Type, Boolean)' failed.
This is because the constructor is marked as SecurityCritical and we cannot use it, in SL5 it was made internal.
Using lamba expression
The next step, I tried the LambdaExpression, and it was the right choose. If you decompile the LambdaExpression class, you will see, that it uses the LambdaCompiler class to generate dynamic methods. The one constructor of LambdaCompiler class looks like this:private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda) { Type[] parameterTypes = CollectionExtensions.AddFirst<Type>((IList<Type>) LambdaCompiler.GetParameterTypes(lambda), typeof (Closure)); DynamicMethod dynamicMethod = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true); this._tree = tree; this._lambda = lambda; this._method = (MethodInfo) dynamicMethod; this._ilg = new OffsetTrackingILGenerator(dynamicMethod.GetILGenerator()); this._hasClosureArgument = true; this._scope = tree.Scopes[(object) lambda]; this._boundConstants = tree.Constants[lambda]; this.InitializeMethod(); }
DynamicMethod dynamicMethod = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true);
The simple example of how to get access to non-public property by using LambdaExpression:
public class Alpha { public Alpha() { } private string _field; protected string Property { get; set; } protected void Method() { } } ... private static TResult CreatePropertyGetter<TResult>(PropertyInfo propertyInfo, Type targetType, Type resultType) { if (!propertyInfo.CanRead) throw new ArgumentException(string.Format("The property {0} in type {1} doesn't have getter.", propertyInfo.PropertyType, propertyInfo.DeclaringType)); ParameterExpression targetExp = Expression.Parameter(targetType, "target"); MemberExpression propertyExp; //Checking if it's a static. if (propertyInfo.GetGetMethod(true).IsStatic) propertyExp = Expression.Property(null, propertyInfo); else { //Checking if target type is not equals to DeclaringType, we need to convert type. if (targetType != propertyInfo.DeclaringType) propertyExp = Expression.Property(Expression.Convert(targetExp, propertyInfo.DeclaringType), propertyInfo); else propertyExp = Expression.Property(targetExp, propertyInfo); } return Expression.Lambda<TResult>(Expression.Convert(propertyExp, resultType), targetExp).Compile(); } const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public; var propertyInfo = typeof(Alpha).GetProperty("Property", flags); var alpha = new Alpha(); var propertyGetter = CreatePropertyGetter<Func<Alpha, string>>(propertyInfo, typeof (Alpha), typeof (string)); string getter = propertyGetter(alpha);
MugenInjection
The MugenInjection has the ExpressionActivator that use a LambdaExpression to access the members. In addition, MugenInjection has the set of extensions that provides access to members, this code demonstrates some of them:using MugenInjection; ..... const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public; var constructorInfo = typeof (Alpha).GetConstructor(Type.EmptyTypes); var propertyInfo = typeof (Alpha).GetProperty("Property", flags); var fieldInfo = typeof (Alpha).GetField("_field", flags); var methodInfo = typeof (Alpha).GetMethod("Method", flags); //Contains provider for member access. // InjectorUtils.ReflectionAccessProvider //Creating new instance. var instance = (Alpha) constructorInfo.CreateInstance(); //Getting the property as object var objValue = propertyInfo.GetValueFast(instance); //Getting the property as string var strValue = propertyInfo.GetValueFast<Alpha, string>(instance); //Setting the property. propertyInfo.SetValueFast(instance, "test"); //Getting the field as object objValue = fieldInfo.GetValueFast(instance); //Getting the field as string strValue = fieldInfo.GetValueFast<Alpha, string>(instance); //Setting the field. fieldInfo.SetValueFast(instance, "test"); //Invoking the method. methodInfo.InvokeFast(instance);
Hi!
Thank you for your useful informations.
However, I note we still can not access private members of external assemblies (as the framework DLLs).
For example, we can not access to the CursorType property of System.Windows.Input.Cursor .
Both Expressions and MugenInjection cause a method access exception (tested in SL 5).
Unfortunately for some of the standard libraries, this method does not work.