| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- using System;
- using System.Reflection;
- using System.Collections;
- using System.Reflection.Emit;
- using System.Threading;
- namespace DynamicProxy
- {
- /// <summary>
- /// Interface that a user defined proxy handler needs to implement. This interface
- /// defines one method that gets invoked by the generated proxy.
- /// </summary>
- public interface IProxyInvocationHandler
- {
- /// <param name="proxy">The instance of the proxy</param>
- /// <param name="method">The method info that can be used to invoke the actual method on the object implementation</param>
- /// <param name="parameters">Parameters to pass to the method</param>
- /// <returns>Object</returns>
- object Invoke(object proxy, MethodInfo method, object[] parameters);
- }
- /// <summary>
- /// Factory class used to cache Types instances
- /// </summary>
- public class MetaDataFactory
- {
- private static Hashtable typeMap = new Hashtable();
- /// <summary>
- /// Class constructor. Private because this is a static class.
- /// </summary>
- private MetaDataFactory()
- {
- }
- ///<summary>
- /// Method to add a new Type to the cache, using the type's fully qualified
- /// name as the key
- ///</summary>
- ///<param name="interfaceType">Type to cache</param>
- public static void Add(Type interfaceType)
- {
- if (interfaceType != null)
- {
- lock (typeMap.SyncRoot)
- {
- if (!typeMap.ContainsKey(interfaceType.FullName))
- {
- typeMap.Add(interfaceType.FullName, interfaceType);
- }
- }
- }
- }
- ///<summary>
- /// Method to return the method of a given type at a specified index.
- ///</summary>
- ///<param name="name">Fully qualified name of the method to return</param>
- ///<param name="i">Index to use to return MethodInfo</param>
- ///<returns>MethodInfo</returns>
- public static MethodInfo GetMethod(string name, int i)
- {
- Type type = null;
- lock (typeMap.SyncRoot)
- {
- type = (Type)typeMap[name];
- }
- return type.GetMethods()[i];
- }
- public static PropertyInfo GetProperty(string name, int i)
- {
- Type type = null;
- lock (typeMap.SyncRoot)
- {
- type = (Type)typeMap[name];
- }
- return type.GetProperties()[i];
- }
- }
- /// <summary>
- /// </summary>
- public class ProxyFactory
- {
- private static ProxyFactory instance;
- private static Object lockObj = new Object();
- private Hashtable typeMap = Hashtable.Synchronized(new Hashtable());
- private static readonly Hashtable opCodeTypeMapper = new Hashtable();
- private const string PROXY_SUFFIX = "Proxy";
- private const string ASSEMBLY_NAME = "ProxyAssembly";
- private const string MODULE_NAME = "ProxyModule";
- private const string HANDLER_NAME = "handler";
- // Initialize the value type mapper. This is needed for methods with intrinsic
- // return types, used in the Emit process.
- static ProxyFactory()
- {
- opCodeTypeMapper.Add(typeof(System.Boolean), OpCodes.Ldind_I1);
- opCodeTypeMapper.Add(typeof(System.Int16), OpCodes.Ldind_I2);
- opCodeTypeMapper.Add(typeof(System.Int32), OpCodes.Ldind_I4);
- opCodeTypeMapper.Add(typeof(System.Int64), OpCodes.Ldind_I8);
- opCodeTypeMapper.Add(typeof(System.Double), OpCodes.Ldind_R8);
- opCodeTypeMapper.Add(typeof(System.Single), OpCodes.Ldind_R4);
- opCodeTypeMapper.Add(typeof(System.UInt16), OpCodes.Ldind_U2);
- opCodeTypeMapper.Add(typeof(System.UInt32), OpCodes.Ldind_U4);
- }
- private ProxyFactory()
- {
- }
- public static ProxyFactory GetInstance()
- {
- if (instance == null)
- {
- CreateInstance();
- }
- return instance;
- }
- private static void CreateInstance()
- {
- lock (lockObj)
- {
- if (instance == null)
- {
- instance = new ProxyFactory();
- }
- }
- }
- public Object Create(IProxyInvocationHandler handler, Type objType, bool isObjInterface)
- {
- string typeName = objType.FullName + PROXY_SUFFIX;
- Type type = (Type)typeMap[typeName];
- // check to see if the type was in the cache. If the type was not cached, then
- // create a new instance of the dynamic type and add it to the cache.
- if (type == null)
- {
- if (isObjInterface)
- {
- type = CreateType(handler, new Type[] { objType }, typeName);
- }
- else
- {
- type = CreateType(handler, objType.GetInterfaces(), typeName);
- }
- typeMap.Add(typeName, type);
- }
- // return a new instance of the type.
- return Activator.CreateInstance(type, new object[] { handler });
- }
- public Object Create(IProxyInvocationHandler handler, Type objType)
- {
- return Create(handler, objType, false);
- }
- private Type CreateType(IProxyInvocationHandler handler, Type[] interfaces, string dynamicTypeName)
- {
- Type retVal = null;
- if (handler != null && interfaces != null)
- {
- Type objType = typeof(System.Object);
- Type handlerType = typeof(IProxyInvocationHandler);
- AppDomain domain = Thread.GetDomain();
- AssemblyName assemblyName = new AssemblyName();
- assemblyName.Name = ASSEMBLY_NAME;
- assemblyName.Version = new Version(1, 0, 0, 0);
- // create a new assembly for this proxy, one that isn't presisted on the file system
- AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(
- assemblyName, AssemblyBuilderAccess.Run);
- // assemblyName, AssemblyBuilderAccess.RunAndSave,"."); // to save it to the disk
- // create a new module for this proxy
- ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(MODULE_NAME);
- // Set the class to be public and sealed
- TypeAttributes typeAttributes =
- TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
- // Gather up the proxy information and create a new type builder. One that
- // inherits from Object and implements the interface passed in
- TypeBuilder typeBuilder = moduleBuilder.DefineType(
- dynamicTypeName, typeAttributes, objType, interfaces);
- // Define a member variable to hold the delegate
- FieldBuilder handlerField = typeBuilder.DefineField(
- HANDLER_NAME, handlerType, FieldAttributes.Private);
- // build a constructor that takes the delegate object as the only argument
- //ConstructorInfo defaultObjConstructor = objType.GetConstructor( new Type[0] );
- ConstructorInfo superConstructor = objType.GetConstructor(new Type[0]);
- ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor(
- MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType });
- #region( "Constructor IL Code" )
- ILGenerator constructorIL = delegateConstructor.GetILGenerator();
- // Load "this"
- constructorIL.Emit(OpCodes.Ldarg_0);
- // Load first constructor parameter
- constructorIL.Emit(OpCodes.Ldarg_1);
- // Set the first parameter into the handler field
- constructorIL.Emit(OpCodes.Stfld, handlerField);
- // Load "this"
- constructorIL.Emit(OpCodes.Ldarg_0);
- // Call the super constructor
- constructorIL.Emit(OpCodes.Call, superConstructor);
- // Constructor return
- constructorIL.Emit(OpCodes.Ret);
- #endregion
- // for every method that the interfaces define, build a corresponding
- // method in the dynamic type that calls the handlers invoke method.
- foreach (Type interfaceType in interfaces)
- {
- GenerateMethod(interfaceType, handlerField, typeBuilder);
- }
- retVal = typeBuilder.CreateType();
- // assemblyBuilder.Save(dynamicTypeName + ".dll");
- }
- return retVal;
- }
- private static readonly MethodInfo INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod("Invoke");
- private static readonly MethodInfo GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod("GetMethod", new Type[] { typeof(string), typeof(int) });
- private void GenerateMethod( Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder ) {
- MetaDataFactory.Add( interfaceType );
- MethodInfo[] interfaceMethods = interfaceType.GetMethods();
- PropertyInfo[] props = interfaceType.GetProperties();
- for ( int i = 0; i < interfaceMethods.Length; i++ ) {
- MethodInfo methodInfo = interfaceMethods[i];
- // Get the method parameters since we need to create an array
- // of parameter types
- ParameterInfo[] methodParams = methodInfo.GetParameters();
- int numOfParams = methodParams.Length;
- Type[] methodParameters = new Type[ numOfParams ];
- // convert the ParameterInfo objects into Type
- for ( int j = 0; j < numOfParams; j++ ) {
- methodParameters[j] = methodParams[j].ParameterType;
- }
- // create a new builder for the method in the interface
- MethodBuilder methodBuilder = typeBuilder.DefineMethod(
- methodInfo.Name,
- /*MethodAttributes.Public | MethodAttributes.Virtual | */ methodInfo.Attributes&~MethodAttributes.Abstract,
- CallingConventions.Standard,
- methodInfo.ReturnType, methodParameters );
- #region( "Handler Method IL Code" )
- ILGenerator methodIL = methodBuilder.GetILGenerator();
-
- // load "this"
- methodIL.Emit( OpCodes.Ldarg_0 );
- // load the handler
- methodIL.Emit( OpCodes.Ldfld, handlerField );
- // load "this" since its needed for the call to invoke
- methodIL.Emit( OpCodes.Ldarg_0 );
- // load the name of the interface, used to get the MethodInfo object
- // from MetaDataFactory
- methodIL.Emit( OpCodes.Ldstr, interfaceType.FullName );
- // load the index, used to get the MethodInfo object
- // from MetaDataFactory
- methodIL.Emit( OpCodes.Ldc_I4, i );
- // invoke GetMethod in MetaDataFactory
- methodIL.Emit( OpCodes.Call, GET_METHODINFO_METHOD);
- // load the number of parameters onto the stack
- methodIL.Emit( OpCodes.Ldc_I4, numOfParams );
- // create a new array, using the size that was just pused on the stack
- methodIL.Emit( OpCodes.Newarr, typeof(object) );
-
- // if we have any parameters, then iterate through and set the values
- // of each element to the corresponding arguments
- for ( int j = 0; j < numOfParams; j++ ) {
- methodIL.Emit( OpCodes.Dup ); // this copies the array
- methodIL.Emit( OpCodes.Ldc_I4, j );
- methodIL.Emit( OpCodes.Ldarg, j + 1 );
- if ( methodParameters[j].IsValueType ) {
- methodIL.Emit( OpCodes.Box, methodParameters[j] );
- }
- methodIL.Emit( OpCodes.Stelem_Ref );
- }
- // call the Invoke method
- methodIL.Emit( OpCodes.Callvirt, INVOKE_METHOD );
-
- if ( methodInfo.ReturnType != typeof(void) ) {
- // if the return type if a value type, then unbox the return value
- // so that we don't get junk.
- if ( methodInfo.ReturnType.IsValueType ) {
- methodIL.Emit( OpCodes.Unbox, methodInfo.ReturnType );
- if ( methodInfo.ReturnType.IsEnum ) {
- methodIL.Emit( OpCodes.Ldind_I4 );
- } else if ( !methodInfo.ReturnType.IsPrimitive ) {
- methodIL.Emit( OpCodes.Ldobj, methodInfo.ReturnType );
- } else {
- methodIL.Emit( (OpCode) opCodeTypeMapper[ methodInfo.ReturnType ] );
- }
- }
- } else {
- // pop the return value that Invoke returned from the stack since
- // the method's return type is void.
- methodIL.Emit( OpCodes.Pop );
- }
-
- // Return
- methodIL.Emit( OpCodes.Ret );
- #endregion
- }
- //for (int i = 0; i < props.Length; i++)
- //{
- // PropertyInfo p = props[i];
- // PropertyBuilder pb = typeBuilder.DefineProperty(p.Name, p.Attributes, p.PropertyType, new Type[] { p.PropertyType });
- // pb.SetGetMethod((MethodBuilder)methodTable[p.GetGetMethod()]);
- // pb.SetSetMethod((MethodBuilder)methodTable[p.GetSetMethod()]);
- //}
- // Iterate through the parent interfaces and recursively call this method
- foreach ( Type parentType in interfaceType.GetInterfaces() ) {
- GenerateMethod( parentType, handlerField, typeBuilder );
- }
- }
- }
- }
|