DynamicProxy.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. using System;
  2. using System.Reflection;
  3. using System.Collections;
  4. using System.Reflection.Emit;
  5. using System.Threading;
  6. namespace DynamicProxy
  7. {
  8. /// <summary>
  9. /// Interface that a user defined proxy handler needs to implement. This interface
  10. /// defines one method that gets invoked by the generated proxy.
  11. /// </summary>
  12. public interface IProxyInvocationHandler
  13. {
  14. /// <param name="proxy">The instance of the proxy</param>
  15. /// <param name="method">The method info that can be used to invoke the actual method on the object implementation</param>
  16. /// <param name="parameters">Parameters to pass to the method</param>
  17. /// <returns>Object</returns>
  18. object Invoke(object proxy, MethodInfo method, object[] parameters);
  19. }
  20. /// <summary>
  21. /// Factory class used to cache Types instances
  22. /// </summary>
  23. public class MetaDataFactory
  24. {
  25. private static Hashtable typeMap = new Hashtable();
  26. /// <summary>
  27. /// Class constructor. Private because this is a static class.
  28. /// </summary>
  29. private MetaDataFactory()
  30. {
  31. }
  32. ///<summary>
  33. /// Method to add a new Type to the cache, using the type's fully qualified
  34. /// name as the key
  35. ///</summary>
  36. ///<param name="interfaceType">Type to cache</param>
  37. public static void Add(Type interfaceType)
  38. {
  39. if (interfaceType != null)
  40. {
  41. lock (typeMap.SyncRoot)
  42. {
  43. if (!typeMap.ContainsKey(interfaceType.FullName))
  44. {
  45. typeMap.Add(interfaceType.FullName, interfaceType);
  46. }
  47. }
  48. }
  49. }
  50. ///<summary>
  51. /// Method to return the method of a given type at a specified index.
  52. ///</summary>
  53. ///<param name="name">Fully qualified name of the method to return</param>
  54. ///<param name="i">Index to use to return MethodInfo</param>
  55. ///<returns>MethodInfo</returns>
  56. public static MethodInfo GetMethod(string name, int i)
  57. {
  58. Type type = null;
  59. lock (typeMap.SyncRoot)
  60. {
  61. type = (Type)typeMap[name];
  62. }
  63. return type.GetMethods()[i];
  64. }
  65. public static PropertyInfo GetProperty(string name, int i)
  66. {
  67. Type type = null;
  68. lock (typeMap.SyncRoot)
  69. {
  70. type = (Type)typeMap[name];
  71. }
  72. return type.GetProperties()[i];
  73. }
  74. }
  75. /// <summary>
  76. /// </summary>
  77. public class ProxyFactory
  78. {
  79. private static ProxyFactory instance;
  80. private static Object lockObj = new Object();
  81. private Hashtable typeMap = Hashtable.Synchronized(new Hashtable());
  82. private static readonly Hashtable opCodeTypeMapper = new Hashtable();
  83. private const string PROXY_SUFFIX = "Proxy";
  84. private const string ASSEMBLY_NAME = "ProxyAssembly";
  85. private const string MODULE_NAME = "ProxyModule";
  86. private const string HANDLER_NAME = "handler";
  87. // Initialize the value type mapper. This is needed for methods with intrinsic
  88. // return types, used in the Emit process.
  89. static ProxyFactory()
  90. {
  91. opCodeTypeMapper.Add(typeof(System.Boolean), OpCodes.Ldind_I1);
  92. opCodeTypeMapper.Add(typeof(System.Int16), OpCodes.Ldind_I2);
  93. opCodeTypeMapper.Add(typeof(System.Int32), OpCodes.Ldind_I4);
  94. opCodeTypeMapper.Add(typeof(System.Int64), OpCodes.Ldind_I8);
  95. opCodeTypeMapper.Add(typeof(System.Double), OpCodes.Ldind_R8);
  96. opCodeTypeMapper.Add(typeof(System.Single), OpCodes.Ldind_R4);
  97. opCodeTypeMapper.Add(typeof(System.UInt16), OpCodes.Ldind_U2);
  98. opCodeTypeMapper.Add(typeof(System.UInt32), OpCodes.Ldind_U4);
  99. }
  100. private ProxyFactory()
  101. {
  102. }
  103. public static ProxyFactory GetInstance()
  104. {
  105. if (instance == null)
  106. {
  107. CreateInstance();
  108. }
  109. return instance;
  110. }
  111. private static void CreateInstance()
  112. {
  113. lock (lockObj)
  114. {
  115. if (instance == null)
  116. {
  117. instance = new ProxyFactory();
  118. }
  119. }
  120. }
  121. public Object Create(IProxyInvocationHandler handler, Type objType, bool isObjInterface)
  122. {
  123. string typeName = objType.FullName + PROXY_SUFFIX;
  124. Type type = (Type)typeMap[typeName];
  125. // check to see if the type was in the cache. If the type was not cached, then
  126. // create a new instance of the dynamic type and add it to the cache.
  127. if (type == null)
  128. {
  129. if (isObjInterface)
  130. {
  131. type = CreateType(handler, new Type[] { objType }, typeName);
  132. }
  133. else
  134. {
  135. type = CreateType(handler, objType.GetInterfaces(), typeName);
  136. }
  137. typeMap.Add(typeName, type);
  138. }
  139. // return a new instance of the type.
  140. return Activator.CreateInstance(type, new object[] { handler });
  141. }
  142. public Object Create(IProxyInvocationHandler handler, Type objType)
  143. {
  144. return Create(handler, objType, false);
  145. }
  146. private Type CreateType(IProxyInvocationHandler handler, Type[] interfaces, string dynamicTypeName)
  147. {
  148. Type retVal = null;
  149. if (handler != null && interfaces != null)
  150. {
  151. Type objType = typeof(System.Object);
  152. Type handlerType = typeof(IProxyInvocationHandler);
  153. AppDomain domain = Thread.GetDomain();
  154. AssemblyName assemblyName = new AssemblyName();
  155. assemblyName.Name = ASSEMBLY_NAME;
  156. assemblyName.Version = new Version(1, 0, 0, 0);
  157. // create a new assembly for this proxy, one that isn't presisted on the file system
  158. AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(
  159. assemblyName, AssemblyBuilderAccess.Run);
  160. // assemblyName, AssemblyBuilderAccess.RunAndSave,"."); // to save it to the disk
  161. // create a new module for this proxy
  162. ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(MODULE_NAME);
  163. // Set the class to be public and sealed
  164. TypeAttributes typeAttributes =
  165. TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
  166. // Gather up the proxy information and create a new type builder. One that
  167. // inherits from Object and implements the interface passed in
  168. TypeBuilder typeBuilder = moduleBuilder.DefineType(
  169. dynamicTypeName, typeAttributes, objType, interfaces);
  170. // Define a member variable to hold the delegate
  171. FieldBuilder handlerField = typeBuilder.DefineField(
  172. HANDLER_NAME, handlerType, FieldAttributes.Private);
  173. // build a constructor that takes the delegate object as the only argument
  174. //ConstructorInfo defaultObjConstructor = objType.GetConstructor( new Type[0] );
  175. ConstructorInfo superConstructor = objType.GetConstructor(new Type[0]);
  176. ConstructorBuilder delegateConstructor = typeBuilder.DefineConstructor(
  177. MethodAttributes.Public, CallingConventions.Standard, new Type[] { handlerType });
  178. #region( "Constructor IL Code" )
  179. ILGenerator constructorIL = delegateConstructor.GetILGenerator();
  180. // Load "this"
  181. constructorIL.Emit(OpCodes.Ldarg_0);
  182. // Load first constructor parameter
  183. constructorIL.Emit(OpCodes.Ldarg_1);
  184. // Set the first parameter into the handler field
  185. constructorIL.Emit(OpCodes.Stfld, handlerField);
  186. // Load "this"
  187. constructorIL.Emit(OpCodes.Ldarg_0);
  188. // Call the super constructor
  189. constructorIL.Emit(OpCodes.Call, superConstructor);
  190. // Constructor return
  191. constructorIL.Emit(OpCodes.Ret);
  192. #endregion
  193. // for every method that the interfaces define, build a corresponding
  194. // method in the dynamic type that calls the handlers invoke method.
  195. foreach (Type interfaceType in interfaces)
  196. {
  197. GenerateMethod(interfaceType, handlerField, typeBuilder);
  198. }
  199. retVal = typeBuilder.CreateType();
  200. // assemblyBuilder.Save(dynamicTypeName + ".dll");
  201. }
  202. return retVal;
  203. }
  204. private static readonly MethodInfo INVOKE_METHOD = typeof(IProxyInvocationHandler).GetMethod("Invoke");
  205. private static readonly MethodInfo GET_METHODINFO_METHOD = typeof(MetaDataFactory).GetMethod("GetMethod", new Type[] { typeof(string), typeof(int) });
  206. private void GenerateMethod( Type interfaceType, FieldBuilder handlerField, TypeBuilder typeBuilder ) {
  207. MetaDataFactory.Add( interfaceType );
  208. MethodInfo[] interfaceMethods = interfaceType.GetMethods();
  209. PropertyInfo[] props = interfaceType.GetProperties();
  210. for ( int i = 0; i < interfaceMethods.Length; i++ ) {
  211. MethodInfo methodInfo = interfaceMethods[i];
  212. // Get the method parameters since we need to create an array
  213. // of parameter types
  214. ParameterInfo[] methodParams = methodInfo.GetParameters();
  215. int numOfParams = methodParams.Length;
  216. Type[] methodParameters = new Type[ numOfParams ];
  217. // convert the ParameterInfo objects into Type
  218. for ( int j = 0; j < numOfParams; j++ ) {
  219. methodParameters[j] = methodParams[j].ParameterType;
  220. }
  221. // create a new builder for the method in the interface
  222. MethodBuilder methodBuilder = typeBuilder.DefineMethod(
  223. methodInfo.Name,
  224. /*MethodAttributes.Public | MethodAttributes.Virtual | */ methodInfo.Attributes&~MethodAttributes.Abstract,
  225. CallingConventions.Standard,
  226. methodInfo.ReturnType, methodParameters );
  227. #region( "Handler Method IL Code" )
  228. ILGenerator methodIL = methodBuilder.GetILGenerator();
  229. // load "this"
  230. methodIL.Emit( OpCodes.Ldarg_0 );
  231. // load the handler
  232. methodIL.Emit( OpCodes.Ldfld, handlerField );
  233. // load "this" since its needed for the call to invoke
  234. methodIL.Emit( OpCodes.Ldarg_0 );
  235. // load the name of the interface, used to get the MethodInfo object
  236. // from MetaDataFactory
  237. methodIL.Emit( OpCodes.Ldstr, interfaceType.FullName );
  238. // load the index, used to get the MethodInfo object
  239. // from MetaDataFactory
  240. methodIL.Emit( OpCodes.Ldc_I4, i );
  241. // invoke GetMethod in MetaDataFactory
  242. methodIL.Emit( OpCodes.Call, GET_METHODINFO_METHOD);
  243. // load the number of parameters onto the stack
  244. methodIL.Emit( OpCodes.Ldc_I4, numOfParams );
  245. // create a new array, using the size that was just pused on the stack
  246. methodIL.Emit( OpCodes.Newarr, typeof(object) );
  247. // if we have any parameters, then iterate through and set the values
  248. // of each element to the corresponding arguments
  249. for ( int j = 0; j < numOfParams; j++ ) {
  250. methodIL.Emit( OpCodes.Dup ); // this copies the array
  251. methodIL.Emit( OpCodes.Ldc_I4, j );
  252. methodIL.Emit( OpCodes.Ldarg, j + 1 );
  253. if ( methodParameters[j].IsValueType ) {
  254. methodIL.Emit( OpCodes.Box, methodParameters[j] );
  255. }
  256. methodIL.Emit( OpCodes.Stelem_Ref );
  257. }
  258. // call the Invoke method
  259. methodIL.Emit( OpCodes.Callvirt, INVOKE_METHOD );
  260. if ( methodInfo.ReturnType != typeof(void) ) {
  261. // if the return type if a value type, then unbox the return value
  262. // so that we don't get junk.
  263. if ( methodInfo.ReturnType.IsValueType ) {
  264. methodIL.Emit( OpCodes.Unbox, methodInfo.ReturnType );
  265. if ( methodInfo.ReturnType.IsEnum ) {
  266. methodIL.Emit( OpCodes.Ldind_I4 );
  267. } else if ( !methodInfo.ReturnType.IsPrimitive ) {
  268. methodIL.Emit( OpCodes.Ldobj, methodInfo.ReturnType );
  269. } else {
  270. methodIL.Emit( (OpCode) opCodeTypeMapper[ methodInfo.ReturnType ] );
  271. }
  272. }
  273. } else {
  274. // pop the return value that Invoke returned from the stack since
  275. // the method's return type is void.
  276. methodIL.Emit( OpCodes.Pop );
  277. }
  278. // Return
  279. methodIL.Emit( OpCodes.Ret );
  280. #endregion
  281. }
  282. //for (int i = 0; i < props.Length; i++)
  283. //{
  284. // PropertyInfo p = props[i];
  285. // PropertyBuilder pb = typeBuilder.DefineProperty(p.Name, p.Attributes, p.PropertyType, new Type[] { p.PropertyType });
  286. // pb.SetGetMethod((MethodBuilder)methodTable[p.GetGetMethod()]);
  287. // pb.SetSetMethod((MethodBuilder)methodTable[p.GetSetMethod()]);
  288. //}
  289. // Iterate through the parent interfaces and recursively call this method
  290. foreach ( Type parentType in interfaceType.GetInterfaces() ) {
  291. GenerateMethod( parentType, handlerField, typeBuilder );
  292. }
  293. }
  294. }
  295. }