About .NET, ASP.NET, MVC, C#, WPF, WCF and everything related to .NET and more.

Access to non-public members in Silverlight

Categories: Programming

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.
Unfortunately, when I had tried to create DynamicMethod I got this exception:
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();
}
Note that:
DynamicMethod dynamicMethod = new DynamicMethod(lambda.Name ?? "lambda_method", 
        lambda.ReturnType, parameterTypes, true);
This is what I wrote above. This class use a constructor, which skips JIT visibility checks on types and members accessed by the MSIL of the dynamic method. Moreover, that means that any method that was created using LambdaExpression has access to the non-public members.
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);

Conclusion

If you need access to non-public members, you can do this using the LambdaExpression or using the extension methods of MugenInjection.

Comments

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.

Leave a Reply
*bold*
_italics_
+underline+
* Bullet List
** Bullet List 2
# Number List
## Number List 2
{"Do not apply formatting"}
{code:language} code here {code:language}.
Supports: aspx c#, c#, c++, html, sql, xml
[url:http://www.example.com]