Travis said:
I have an internal factory class with methods I want to call from
another DLL using reflection. However, trying to do so results in a
MethodAccessException.
I am assuming this is because library vendors don't want people
accessing internal members. Is there a way around this? [...]
You need to be more specific. What is the context? A full-trust
application should have complete access to type members through
reflection, provided you are passing the appropriate binding flags.
If for some reason your assembly has restrictions on reflection, you may
be able to use the [InternalsVisibleTo] attribute to allow your dynamic
assembly to have access to the internals of your main assembly.
Personally, I would look for a solution that somehow avoids the need for
the dynamic types to have to call the factory directly. After all,
there's some approved way for other code to access the factory, directly
or indirectly, right? If the dynamic types went through the same
mechanism, they shouldn't need any more access to the assembly members
than any other client code would.
But barring that, you should be able to get the reflection to work fine.
If the above doesn't provide enough information, then provide a
concise-but-complete code example that reliably demonstrates the issue.
Pete
The unfortunate thing is that code using System.Reflection.Emit is
rarely concise. Outside of my dynamically generated classes, the only
other code that knows about the factory is code in the same DLL. It is
not like my factory is dynamically generated. I suppose I could give
you a code snippet. It is the factory class that generates
IEqualityComparers. It uses type IMetaMapping to provide information
about the type the comparer is being made for. In my situation, I am
creating an IEqualityComparer for the primary key fields of types.
// EqualityComparerRegistry.cs
using System;
using System.Reflection.Emit;
using System.Reflection;
namespace Earthworm
{
/// <summary>
/// Provides access to a single repository of dynamically
generated IEqualityComparers.
/// </summary>
internal static class EqualityComparerRegistry
{
private readonly static AssemblyBuilder _assemblyBuilder;
private readonly static ModuleBuilder _moduleBuilder;
/// <summary>
/// Initializes the AssemblyBuilder and the ModuleBuilder.
/// </summary>
static EqualityComparerRegistry()
{
AssemblyName assemblyName = new
AssemblyName("Earthworm.EqualityComparers");
_assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.RunAndCollect);
_moduleBuilder =
_assemblyBuilder.DefineDynamicModule("EarthwormEqualityComparers");
}
/// <summary>
/// Gets the dynamic module to add types to.
/// </summary>
public static ModuleBuilder Module
{
get { return _moduleBuilder; }
}
}
}
// EqualityComparerFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using Earthworm.Properties;
namespace Earthworm
{
/// <summary>
/// Creates IEqualityComparers that use the unique identifiers of
/// objects to determine equality.
/// </summary>
public sealed class EqualityComparerFactory
{
private readonly MetaMappingLookup _lookup;
private readonly Dictionary<Type, object> _cache;
/// <summary>
/// Creates a new EqualityComparerFactory.
/// </summary>
/// <param name="lookup">Provides information about the
uniquely identifying fields.</param>
public EqualityComparerFactory(MetaMappingLookup lookup)
{
_lookup = lookup;
_cache = new Dictionary<Type, object>();
}
/// <summary>
/// Creates a IEqualityComparer based on the type's unique
identifiers.
/// </summary>
/// <typeparam name="T">The type of the object to create the
comparer for.</typeparam>
/// <returns>The generated IEqualityComparer.</returns>
public IEqualityComparer<T> CreateComparer<T>(bool
holdHierachy)
{
object comparer;
if (!_cache.TryGetValue(typeof(T), out comparer))
{
IMetaMapping mapping = _lookup.GetMapping(typeof(T));
if (mapping == null)
{
return EqualityComparer<T>.Default;
}
IMemberMapping[] memberMappings =
mapping.MemberMappings.ToArray();
if (memberMappings.Length == 0)
{
return EqualityComparer<T>.Default;
}
IMemberMapping[] primaryKeyMappings =
memberMappings.Where(member => member.IsPrimaryKey).ToArray();
if (primaryKeyMappings.Length != 0)
{
memberMappings = primaryKeyMappings;
}
string typeName = typeof(T).Name + "EqualityComparer"
+ Guid.NewGuid().ToString("N");
TypeBuilder typeBuilder =
EqualityComparerRegistry.Module.DefineType(typeName,
TypeAttributes.Public | TypeAttributes.Sealed);
typeBuilder.AddInterfaceImplementation(typeof(IEqualityComparer<T>));
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
FieldInfo fieldInfo =
typeBuilder.DefineField("_factory", typeof(EqualityComparerFactory),
FieldAttributes.Private);
defineEquals<T>(typeBuilder, memberMappings,
fieldInfo, holdHierachy);
defineGetHashCode<T>(typeBuilder, memberMappings,
fieldInfo, holdHierachy);
Type type = typeBuilder.CreateType();
comparer = Activator.CreateInstance(type);
type.GetField(fieldInfo.Name, BindingFlags.NonPublic |
BindingFlags.Instance).SetValue(comparer, this);
_cache[typeof(T)] = comparer;
}
return (IEqualityComparer<T>)comparer;
}
private void defineEquals<T>(TypeBuilder typeBuilder,
IMemberMapping[] mappings, FieldInfo fieldInfo, bool holdHierachy)
{
MethodInfo equalInfo =
typeof(IEqualityComparer<T>).GetMethod("Equals");
MethodBuilder equals = buildMethod(typeBuilder,
equalInfo);
ILGenerator generator = equals.GetILGenerator();
LocalBuilder result =
generator.DeclareLocal(typeof(Int32));
Label endOfMethod = generator.DefineLabel();
if (holdHierachy)
{
MethodInfo getTypeInfo =
typeof(Object).GetMethod("GetType");
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, getTypeInfo);
generator.Emit(OpCodes.Ldarg_2);
generator.Emit(OpCodes.Callvirt, getTypeInfo);
generator.Emit(OpCodes.Ceq);
generator.Emit(OpCodes.Brfalse, endOfMethod);
}
MethodInfo objectEquals =
typeof(Object).GetMethod("Equals", BindingFlags.Static |
BindingFlags.Public);
MethodInfo createComparerMethod =
typeof(EqualityComparerFactory).GetMethod("CreateComparer");
foreach (IMemberMapping mapping in mappings)
{
if (_lookup.Contains(mapping.MemberType))
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldInfo);
generator.Emit(OpCodes.Ldc_I4_0); // false
MethodInfo genericCreateComparerMethod =
createComparerMethod.MakeGenericMethod(new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt,
genericCreateComparerMethod);
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
generator.Emit(OpCodes.Ldarg_2);
emitMemberValue(generator, mapping);
Type comparerType =
typeof(IEqualityComparer<>).MakeGenericType(mapping.MemberType);
MethodInfo equalsMethod =
comparerType.GetMethod("Equals", new Type[] { mapping.MemberType,
mapping.MemberType });
generator.Emit(OpCodes.Callvirt, equalsMethod);
}
else
{
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
if (mapping.MemberType.IsValueType)
{
generator.Emit(OpCodes.Box,
mapping.MemberType);
}
generator.Emit(OpCodes.Ldarg_2);
emitMemberValue(generator, mapping);
if (mapping.MemberType.IsValueType)
{
generator.Emit(OpCodes.Box,
mapping.MemberType);
}
generator.Emit(OpCodes.Call, objectEquals);
}
generator.Emit(OpCodes.Brfalse, endOfMethod);
}
generator.Emit(OpCodes.Ldc_I4, 1);
generator.Emit(OpCodes.Stloc, result); // if we get this
far, we know we are equal
generator.MarkLabel(endOfMethod);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
}
private void defineGetHashCode<T>(TypeBuilder typeBuilder,
IMemberMapping[] mappings, FieldInfo fieldInfo, bool holdHierachy)
{
MethodInfo getHashCodeInfo =
typeof(IEqualityComparer<T>).GetMethod("GetHashCode");
MethodBuilder getHashCode = buildMethod(typeBuilder,
getHashCodeInfo);
ILGenerator generator = getHashCode.GetILGenerator();
LocalBuilder result =
generator.DeclareLocal(typeof(Int32));
MethodInfo objectGetHashCode =
typeof(Object).GetMethod("GetHashCode");
if (holdHierachy)
{
MethodInfo getTypeInfo =
typeof(Object).GetMethod("GetType");
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, getTypeInfo);
generator.Emit(OpCodes.Callvirt, objectGetHashCode);
generator.Emit(OpCodes.Stloc, result);
}
MethodInfo createComparerMethod =
typeof(EqualityComparerFactory).GetMethod("CreateComparer");
foreach (IMemberMapping mapping in mappings)
{
Label skip = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
generator.Emit(OpCodes.Brfalse, skip);
if (_lookup.Contains(mapping.MemberType))
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldInfo);
generator.Emit(OpCodes.Ldc_I4_0); // false
MethodInfo genericCreateComparerMethod =
createComparerMethod.MakeGenericMethod(new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt,
genericCreateComparerMethod);
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
Type comparerType =
typeof(IEqualityComparer<>).MakeGenericType(new Type[]
{ mapping.MemberType });
MethodInfo comparerGetHashCode =
comparerType.GetMethod("GetHashCode", new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt,
comparerGetHashCode);
}
else
{
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
if (mapping.MemberType.IsValueType)
{
generator.Emit(OpCodes.Box,
mapping.MemberType);
}
generator.Emit(OpCodes.Callvirt,
objectGetHashCode);
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Xor);
generator.Emit(OpCodes.Stloc, result);
generator.MarkLabel(skip);
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
}
private static MethodBuilder buildMethod(TypeBuilder
typeBuilder, MethodInfo methodInfo)
{
MethodAttributes attributes = methodInfo.Attributes;
attributes &= ~(MethodAttributes.VtableLayoutMask |
MethodAttributes.Abstract);
MethodBuilder methodBuilder =
typeBuilder.DefineMethod(methodInfo.Name,
attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
methodInfo.GetParameters().Select(parameter =>
parameter.ParameterType).ToArray());
return methodBuilder;
}
private static void emitMemberValue(ILGenerator generator,
IMemberMapping mapping)
{
if (mapping.MemberInfo.MemberType == MemberTypes.Field)
{
generator.Emit(OpCodes.Ldfld,
(FieldInfo)mapping.MemberInfo);
}
else
{
PropertyInfo propertyInfo =
(PropertyInfo)mapping.MemberInfo;
MethodInfo getterInfo = propertyInfo.GetGetMethod();
generator.Emit(OpCodes.Callvirt, getterInfo);
}
}
}
}