From: Travis Parks on 6 Jun 2010 12:12 Hello: 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? In my situation, I have a factory class that generates IEqualityComparer<T> types at runtime using System.Reflection.Emit. The comparers can be recursive, meaning that they can rely on other dynamically generated comparers. In order to do so, they need to call methods on the factory. What are my options? My factory has to be internal or public. I'd rather avoid exposing this class if I can. I am hoping there is some flag or attribute that tells the runtime to relax. Thanks, Travis Parks
From: Peter Duniho on 6 Jun 2010 16:56 Travis Parks wrote: > Hello: > > 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
From: Travis Parks on 6 Jun 2010 19:13 On Jun 6, 2:56 pm, Peter Duniho <NpOeStPe...(a)NnOwSlPiAnMk.com> wrote: > Travis Parks wrote: > > Hello: > > > 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); } } } }
From: Arne Vajhøj on 6 Jun 2010 21:54 On 06-06-2010 12:12, Travis Parks wrote: > 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? > > In my situation, I have a factory class that generates > IEqualityComparer<T> types at runtime using System.Reflection.Emit. > The comparers can be recursive, meaning that they can rely on other > dynamically generated comparers. In order to do so, they need to call > methods on the factory. > > What are my options? My factory has to be internal or public. I'd > rather avoid exposing this class if I can. I am hoping there is some > flag or attribute that tells the runtime to relax. How do you attempt to call the method? Normally the accessibility rules does not apply to reflection. The following code runs: using System; using System.Reflection; namespace E { public class Test { private void NoAccessMethod() { Console.WriteLine("What happended here?"); } } public class Program { public static void Main(string[] args) { Test o = new Test(); //o.NoAccessMethod(); MethodInfo mi = o.GetType().GetMethod("NoAccessMethod", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); mi.Invoke(o, new object[0]); } } } Arne
From: Travis Parks on 7 Jun 2010 00:00 On Jun 6, 7:54 pm, Arne Vajhøj <a...(a)vajhoej.dk> wrote: > On 06-06-2010 12:12, Travis Parks wrote: > > > > > > > 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? > > > In my situation, I have a factory class that generates > > IEqualityComparer<T> types at runtime using System.Reflection.Emit. > > The comparers can be recursive, meaning that they can rely on other > > dynamically generated comparers. In order to do so, they need to call > > methods on the factory. > > > What are my options? My factory has to be internal or public. I'd > > rather avoid exposing this class if I can. I am hoping there is some > > flag or attribute that tells the runtime to relax. > > How do you attempt to call the method? > > Normally the accessibility rules does not apply to reflection. > > The following code runs: > > using System; > using System.Reflection; > > namespace E > { > public class Test > { > private void NoAccessMethod() > { > Console.WriteLine("What happended here?"); > } > } > public class Program > { > public static void Main(string[] args) > { > Test o = new Test(); > //o.NoAccessMethod(); > MethodInfo mi = o.GetType().GetMethod("NoAccessMethod", > BindingFlags.DeclaredOnly | BindingFlags.NonPublic | > BindingFlags.Instance, null, new Type[0], null); > mi.Invoke(o, new object[0]); > } > } > > } > > Arne- Hide quoted text - > > - Show quoted text - Instead of private, I'm calling a method in an internal class. That is the only difference. MethodInfo createComparerMethod = typeof(EqualityComparerFactory).GetMethod("CreateComparer"); // takes T generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, fieldInfo); // each comparer holds a reference to the factory, which is supposed to be internal generator.Emit(OpCodes.Ldc_I4_0); // false MethodInfo genericCreateComparerMethod = createComparerMethod.MakeGenericMethod(new Type[] { mapping.MemberType }); generator.Emit(OpCodes.Callvirt, genericCreateComparerMethod); Could the fact that the method is generic make a difference? The factory itself is nongeneric, but the CreateComparer method takes a T. I know from experience calling non-public methods is perfectly valid. I have only run into this issue when doing stuff with System.Reflection.Emit. Perhaps dynamically generated types have access restrictions? Beats me. Thanks, Travis Parks
|
Next
|
Last
Pages: 1 2 3 4 Prev: CA2000 Warning call Dispose on object Next: Hash table with coarse integer keys |