| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.ComponentModel; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| namespace EmitCreateDynamicProxy | |||||
| { | |||||
| class DDD : INotifyPropertyChanged | |||||
| { | |||||
| public event PropertyChangedEventHandler PropertyChanged; | |||||
| public string Name { get => _Name; set { _Name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("uuu")); } } | |||||
| private string _Name = ""; | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.ComponentModel; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Reflection.Emit; | |||||
| using System.Text; | |||||
| using System.Threading; | |||||
| namespace EmitCreateDynamicProxy | |||||
| { | |||||
| public class DynamicProxyGenerator | |||||
| { | |||||
| private const string DynamicAssemblyName = "DynamicAssembly";//动态程序集名称 | |||||
| private const string DynamicModuleName = "DynamicAssemblyModule"; | |||||
| private const string DynamicModuleDllName = "DynamicAssembly.dll";//动态模块名称 | |||||
| private const string ProxyClassNameFormater = "{0}Proxy"; | |||||
| private const string ModifiedPropertyNamesFieldName = "ModifiedPropertyNames"; | |||||
| private const MethodAttributes GetSetMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.CheckAccessOnOverride | MethodAttributes.Virtual | MethodAttributes.HideBySig; | |||||
| /// <summary> | |||||
| /// 创建动态程序集,返回AssemblyBuilder | |||||
| /// </summary> | |||||
| /// <param name="isSavaDll"></param> | |||||
| /// <returns></returns> | |||||
| private static AssemblyBuilder DefineDynamicAssembly(bool isSavaDll = false) | |||||
| { | |||||
| //动态创建程序集 | |||||
| AssemblyName DemoName = new AssemblyName(DynamicAssemblyName); | |||||
| AssemblyBuilderAccess assemblyBuilderAccess = isSavaDll ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run; | |||||
| AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(DemoName, assemblyBuilderAccess); | |||||
| return dynamicAssembly; | |||||
| } | |||||
| /// <summary> | |||||
| /// 创建动态模块,返回ModuleBuilder | |||||
| /// </summary> | |||||
| /// <param name="isSavaDll"></param> | |||||
| /// <returns>ModuleBuilder</returns> | |||||
| private static ModuleBuilder DefineDynamicModule(AssemblyBuilder dynamicAssembly, bool isSavaDll = false) | |||||
| { | |||||
| ModuleBuilder moduleBuilder = null; | |||||
| //动态创建模块 | |||||
| if (isSavaDll) | |||||
| moduleBuilder = dynamicAssembly.DefineDynamicModule(DynamicModuleName, DynamicModuleDllName); | |||||
| else | |||||
| moduleBuilder = dynamicAssembly.DefineDynamicModule(DynamicModuleName); | |||||
| return moduleBuilder; | |||||
| } | |||||
| /// <summary> | |||||
| /// 创建动态代理类,重写属性Get Set 方法,并监控属性的Set方法,把变更的属性名加入到list集合中,需要监控的属性必须是virtual | |||||
| /// 如果你想保存修改的属性名和属性值,修改Set方法的IL实现 | |||||
| /// </summary> | |||||
| /// <typeparam name="T"></typeparam> | |||||
| /// <param name="isSavaDynamicModule"></param> | |||||
| /// <returns></returns> | |||||
| public static T CreateDynamicProxy<T>(bool isSavaDynamicModule = false) | |||||
| { | |||||
| Type modifiedPropertyNamesType = typeof(HashSet<string>); | |||||
| Type typeNeedProxy = typeof(T); | |||||
| AssemblyBuilder assemblyBuilder = DefineDynamicAssembly(isSavaDynamicModule); | |||||
| //动态创建模块 | |||||
| ModuleBuilder moduleBuilder = DefineDynamicModule(assemblyBuilder, isSavaDynamicModule); | |||||
| string proxyClassName = string.Format(ProxyClassNameFormater, typeNeedProxy.Name); | |||||
| //动态创建类代理 | |||||
| TypeBuilder typeBuilderProxy = moduleBuilder.DefineType(proxyClassName, TypeAttributes.Public, typeNeedProxy); | |||||
| //定义一个变量存放属性变更名 | |||||
| FieldBuilder fbModifiedPropertyNames = typeBuilderProxy.DefineField(ModifiedPropertyNamesFieldName, modifiedPropertyNamesType, FieldAttributes.Public); | |||||
| //定义接口 | |||||
| var pt = typeof(PropertyChangedEventHandler); | |||||
| typeBuilderProxy.AddInterfaceImplementation(typeof(INotifyPropertyChanged)); | |||||
| var eil = typeBuilderProxy.DefineEvent("PropertyChanged", EventAttributes.None, typeof(PropertyChangedEventHandler)); | |||||
| //add field | |||||
| var eb = typeBuilderProxy.DefineField("PropertyChanged", typeof(PropertyChangedEventHandler), FieldAttributes.Private); | |||||
| var compareExchange = GetGenericMethod(typeof(Interlocked), "CompareExchange");// finds the correct method to call. | |||||
| // thanks to @marc, create the specific method with the correct type | |||||
| compareExchange = compareExchange.MakeGenericMethod(typeof(PropertyChangedEventHandler)); | |||||
| //add methoed | |||||
| var mas = MethodAttributes.Public | MethodAttributes.HideBySig | |||||
| | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.Final; | |||||
| var mb = typeBuilderProxy.DefineMethod("add_PropertyChanged", mas, typeof(void), new Type[] { pt }); | |||||
| mb.DefineParameter(0, ParameterAttributes.Retval, null); | |||||
| mb.DefineParameter(1, ParameterAttributes.In, "value"); | |||||
| var il = mb.GetILGenerator(); | |||||
| il.DeclareLocal(pt); | |||||
| il.DeclareLocal(pt); | |||||
| il.DeclareLocal(pt); | |||||
| il.Emit(OpCodes.Ldarg_0); | |||||
| il.Emit(OpCodes.Ldfld, eb); | |||||
| il.Emit(OpCodes.Stloc_0); | |||||
| var lable = il.DefineLabel(); | |||||
| il.MarkLabel(lable); | |||||
| il.Emit(OpCodes.Ldloc_0); | |||||
| il.Emit(OpCodes.Stloc_1); | |||||
| il.Emit(OpCodes.Ldloc_1); | |||||
| il.Emit(OpCodes.Ldarg_1); | |||||
| il.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new Type[] { typeof(Delegate), typeof(Delegate) })); | |||||
| il.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler)); | |||||
| il.Emit(OpCodes.Stloc_2); | |||||
| il.Emit(OpCodes.Ldarg_0); | |||||
| il.Emit(OpCodes.Ldflda, eb); | |||||
| il.Emit(OpCodes.Ldloc_2); | |||||
| il.Emit(OpCodes.Ldloc_1); | |||||
| il.Emit(OpCodes.Call, compareExchange); | |||||
| il.Emit(OpCodes.Stloc_0); | |||||
| il.Emit(OpCodes.Ldloc_0); | |||||
| il.Emit(OpCodes.Ldloc_1); | |||||
| il.Emit(OpCodes.Bne_Un_S, lable); | |||||
| il.Emit(OpCodes.Ret); | |||||
| //remove event | |||||
| mb = typeBuilderProxy.DefineMethod("remove_PropertyChanged", mas, typeof(void), new Type[] { pt }); | |||||
| mb.DefineParameter(0, ParameterAttributes.Retval, null); | |||||
| mb.DefineParameter(1, ParameterAttributes.In, "value"); | |||||
| il = mb.GetILGenerator(); | |||||
| il.DeclareLocal(pt); | |||||
| il.DeclareLocal(pt); | |||||
| il.DeclareLocal(pt); | |||||
| il.Emit(OpCodes.Ldarg_0); | |||||
| il.Emit(OpCodes.Ldfld, eb); | |||||
| il.Emit(OpCodes.Stloc_0); | |||||
| lable = il.DefineLabel(); | |||||
| il.MarkLabel(lable); | |||||
| il.Emit(OpCodes.Ldloc_0); | |||||
| il.Emit(OpCodes.Stloc_1); | |||||
| il.Emit(OpCodes.Ldloc_1); | |||||
| il.Emit(OpCodes.Ldarg_1); | |||||
| il.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new Type[] { typeof(Delegate), typeof(Delegate) })); | |||||
| il.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler)); | |||||
| il.Emit(OpCodes.Stloc_2); | |||||
| il.Emit(OpCodes.Ldarg_0); | |||||
| il.Emit(OpCodes.Ldflda, eb); | |||||
| il.Emit(OpCodes.Ldloc_2); | |||||
| il.Emit(OpCodes.Ldloc_1); | |||||
| il.Emit(OpCodes.Call, compareExchange); | |||||
| il.Emit(OpCodes.Stloc_0); | |||||
| il.Emit(OpCodes.Ldloc_0); | |||||
| il.Emit(OpCodes.Ldloc_1); | |||||
| il.Emit(OpCodes.Bne_Un_S, lable); | |||||
| il.Emit(OpCodes.Ret); | |||||
| /* | |||||
| * 构造函数 实例化 ModifiedPropertyNames,生成类似于下面的代码 | |||||
| ModifiedPropertyNames = new List<string>(); | |||||
| */ | |||||
| ConstructorBuilder constructorBuilder = typeBuilderProxy.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null); | |||||
| ILGenerator ilgCtor = constructorBuilder.GetILGenerator(); | |||||
| ilgCtor.Emit(OpCodes.Ldarg_0);//加载当前类 | |||||
| ilgCtor.Emit(OpCodes.Newobj, modifiedPropertyNamesType.GetConstructor(new Type[0]));//实例化对象入栈 | |||||
| ilgCtor.Emit(OpCodes.Stfld, fbModifiedPropertyNames);//设置fbModifiedPropertyNames值,为刚入栈的实例化对象 | |||||
| ilgCtor.Emit(OpCodes.Ret);//返回 | |||||
| //获取被代理对象的所有属性,循环属性进行重写 | |||||
| PropertyInfo[] properties = typeNeedProxy.GetProperties(); | |||||
| foreach (PropertyInfo propertyInfo in properties) | |||||
| { | |||||
| string propertyName = propertyInfo.Name; | |||||
| Type typePepropertyInfo = propertyInfo.PropertyType; | |||||
| //动态创建字段和属性 | |||||
| FieldBuilder fieldBuilder = typeBuilderProxy.DefineField("_" + propertyName, typePepropertyInfo, FieldAttributes.Private); | |||||
| PropertyBuilder propertyBuilder = typeBuilderProxy.DefineProperty(propertyName, PropertyAttributes.SpecialName, typePepropertyInfo, null); | |||||
| //重写属性的Get Set方法 | |||||
| var methodGet = typeBuilderProxy.DefineMethod("get_" + propertyName, GetSetMethodAttributes, typePepropertyInfo, Type.EmptyTypes); | |||||
| var methodSet = typeBuilderProxy.DefineMethod("set_" + propertyName, GetSetMethodAttributes, null, new Type[] { typePepropertyInfo }); | |||||
| //il of get method | |||||
| var ilGetMethod = methodGet.GetILGenerator(); | |||||
| ilGetMethod.Emit(OpCodes.Ldarg_0); | |||||
| ilGetMethod.Emit(OpCodes.Ldfld, fieldBuilder); | |||||
| ilGetMethod.Emit(OpCodes.Ret); | |||||
| //il of set method | |||||
| ILGenerator ilSetMethod = methodSet.GetILGenerator(); | |||||
| ilSetMethod.Emit(OpCodes.Ldarg_0); | |||||
| ilSetMethod.Emit(OpCodes.Ldarg_1); | |||||
| ilSetMethod.Emit(OpCodes.Stfld, fieldBuilder); | |||||
| ilSetMethod.Emit(OpCodes.Ldarg_0); | |||||
| ilSetMethod.Emit(OpCodes.Ldfld, fbModifiedPropertyNames); | |||||
| ilSetMethod.Emit(OpCodes.Ldstr, propertyInfo.Name); | |||||
| ilSetMethod.Emit(OpCodes.Callvirt, modifiedPropertyNamesType.GetMethod("Add", new Type[] { typeof(string) })); | |||||
| ilSetMethod.Emit(OpCodes.Pop); | |||||
| ilSetMethod.Emit(OpCodes.Ret); | |||||
| //设置属性的Get Set方法 | |||||
| propertyBuilder.SetGetMethod(methodGet); | |||||
| propertyBuilder.SetSetMethod(methodSet); | |||||
| } | |||||
| //使用动态类创建类型 | |||||
| Type proxyClassType = typeBuilderProxy.CreateType(); | |||||
| //保存动态创建的程序集 | |||||
| if (isSavaDynamicModule) | |||||
| assemblyBuilder.Save(DynamicModuleDllName); | |||||
| //创建类实例 | |||||
| var instance = Activator.CreateInstance(proxyClassType); | |||||
| return (T)instance; | |||||
| } | |||||
| /// <summary> | |||||
| /// 获取属性的变更名称, | |||||
| /// 此处只检测调用了Set方法的属性,不会检测值是否真的有变 | |||||
| /// </summary> | |||||
| /// <param name="obj"></param> | |||||
| /// <returns></returns> | |||||
| public static HashSet<string> GetModifiedProperties(object obj) | |||||
| { | |||||
| FieldInfo fieldInfo = obj.GetType().GetField(ModifiedPropertyNamesFieldName); | |||||
| if (fieldInfo == null) return null; | |||||
| object value = fieldInfo.GetValue(obj); | |||||
| return value as HashSet<string>; | |||||
| } | |||||
| private static MethodInfo GetGenericMethod(Type type, string methodName) | |||||
| { | |||||
| var q = from m in type.GetMethods() | |||||
| where m.Name == methodName && m.IsGenericMethod | |||||
| select m; | |||||
| return q.FirstOrDefault(); | |||||
| } | |||||
| } | |||||
| } |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
| <PropertyGroup> | |||||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||||
| <Platform Condition=" '$(Platform)' == '' ">x86</Platform> | |||||
| <ProductVersion>8.0.30703</ProductVersion> | |||||
| <SchemaVersion>2.0</SchemaVersion> | |||||
| <ProjectGuid>{BDA6268A-ADF2-4DA3-8053-81CA4AD007B0}</ProjectGuid> | |||||
| <OutputType>Exe</OutputType> | |||||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||||
| <RootNamespace>EmitCreateDynamicProxy</RootNamespace> | |||||
| <AssemblyName>EmitCreateDynamicProxy</AssemblyName> | |||||
| <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | |||||
| <TargetFrameworkProfile>Client</TargetFrameworkProfile> | |||||
| <FileAlignment>512</FileAlignment> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> | |||||
| <PlatformTarget>x86</PlatformTarget> | |||||
| <DebugSymbols>true</DebugSymbols> | |||||
| <DebugType>full</DebugType> | |||||
| <Optimize>false</Optimize> | |||||
| <OutputPath>bin\Debug\</OutputPath> | |||||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> | |||||
| <PlatformTarget>x86</PlatformTarget> | |||||
| <DebugType>pdbonly</DebugType> | |||||
| <Optimize>true</Optimize> | |||||
| <OutputPath>bin\Release\</OutputPath> | |||||
| <DefineConstants>TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <Reference Include="System" /> | |||||
| <Reference Include="System.Core" /> | |||||
| <Reference Include="System.Xml.Linq" /> | |||||
| <Reference Include="System.Data.DataSetExtensions" /> | |||||
| <Reference Include="Microsoft.CSharp" /> | |||||
| <Reference Include="System.Data" /> | |||||
| <Reference Include="System.Xml" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | |||||
| <Compile Include="DDD.cs" /> | |||||
| <Compile Include="DynamicProxyGenerator.cs" /> | |||||
| <Compile Include="Model.cs" /> | |||||
| <Compile Include="Program.cs" /> | |||||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||||
| </ItemGroup> | |||||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||||
| Other similar extension points exist, see Microsoft.Common.targets. | |||||
| <Target Name="BeforeBuild"> | |||||
| </Target> | |||||
| <Target Name="AfterBuild"> | |||||
| </Target> | |||||
| --> | |||||
| </Project> |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| namespace EmitCreateDynamicProxy | |||||
| { | |||||
| public class Model | |||||
| { | |||||
| public virtual string LoginName { get; set; } | |||||
| public virtual string Password { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Reflection.Emit; | |||||
| namespace EmitCreateDynamicProxy | |||||
| { | |||||
| class Program | |||||
| { | |||||
| static void Main(string[] args) | |||||
| { | |||||
| //var command = Proxy.Of<Command>(); | |||||
| //command.Execute(); | |||||
| //Console.WriteLine("Hi, Dennis, great, we got the interceptor works."); | |||||
| //Console.ReadLine(); | |||||
| Model m = DynamicProxyGenerator.CreateDynamicProxy<Model>(true); | |||||
| m.LoginName = "ddd"; | |||||
| HashSet<string> modifiedPropertyNames = DynamicProxyGenerator.GetModifiedProperties(m) as HashSet<string>; | |||||
| } | |||||
| } | |||||
| public class Command | |||||
| { | |||||
| public virtual void Execute() | |||||
| { | |||||
| Console.WriteLine("Command executing..."); | |||||
| Console.WriteLine("Hello Kitty!"); | |||||
| Console.WriteLine("Command executed."); | |||||
| } | |||||
| } | |||||
| public class Interceptor | |||||
| { | |||||
| public object Invoke(object @object, string @method, object[] parameters) | |||||
| { | |||||
| Console.WriteLine( | |||||
| string.Format("Interceptor does something before invoke [{0}]...", @method)); | |||||
| var retObj = @object.GetType().GetMethod(@method).Invoke(@object, parameters); | |||||
| Console.WriteLine( | |||||
| string.Format("Interceptor does something after invoke [{0}]...", @method)); | |||||
| return retObj; | |||||
| } | |||||
| } | |||||
| public class Proxy | |||||
| { | |||||
| public static T Of<T>() where T : class, new() | |||||
| { | |||||
| string nameOfAssembly = typeof(T).Name + "ProxyAssembly"; | |||||
| string nameOfModule = typeof(T).Name + "ProxyModule"; | |||||
| string nameOfType = typeof(T).Name + "Proxy"; | |||||
| var assemblyName = new AssemblyName(nameOfAssembly); | |||||
| var assembly = AppDomain.CurrentDomain | |||||
| .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); | |||||
| var moduleBuilder = assembly.DefineDynamicModule(nameOfModule); | |||||
| var typeBuilder = moduleBuilder.DefineType( | |||||
| nameOfType, TypeAttributes.Public, typeof(T)); | |||||
| InjectInterceptor<T>(typeBuilder); | |||||
| var t = typeBuilder.CreateType(); | |||||
| return Activator.CreateInstance(t) as T; | |||||
| } | |||||
| private static void InjectInterceptor<T>(TypeBuilder typeBuilder) | |||||
| { | |||||
| // ---- define fields ---- | |||||
| var fieldInterceptor = typeBuilder.DefineField( | |||||
| "_interceptor", typeof(Interceptor), FieldAttributes.Private); | |||||
| // ---- define costructors ---- | |||||
| var constructorBuilder = typeBuilder.DefineConstructor( | |||||
| MethodAttributes.Public, CallingConventions.Standard, null); | |||||
| var ilOfCtor = constructorBuilder.GetILGenerator(); | |||||
| ilOfCtor.Emit(OpCodes.Ldarg_0); | |||||
| ilOfCtor.Emit(OpCodes.Newobj, typeof(Interceptor).GetConstructor(new Type[0])); | |||||
| ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor); | |||||
| ilOfCtor.Emit(OpCodes.Ret); | |||||
| // ---- define methods ---- | |||||
| var methodsOfType = typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance); | |||||
| for (var i = 0; i < methodsOfType.Length; i++) | |||||
| { | |||||
| var method = methodsOfType[i]; | |||||
| var methodParameterTypes = | |||||
| method.GetParameters().Select(p => p.ParameterType).ToArray(); | |||||
| var methodBuilder = typeBuilder.DefineMethod( | |||||
| method.Name, | |||||
| MethodAttributes.Public | MethodAttributes.Virtual, | |||||
| CallingConventions.Standard, | |||||
| method.ReturnType, | |||||
| methodParameterTypes); | |||||
| var ilOfMethod = methodBuilder.GetILGenerator(); | |||||
| ilOfMethod.Emit(OpCodes.Ldarg_0); | |||||
| ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor); | |||||
| // create instance of T | |||||
| ilOfMethod.Emit(OpCodes.Newobj, typeof(T).GetConstructor(new Type[0])); | |||||
| ilOfMethod.Emit(OpCodes.Ldstr, method.Name); | |||||
| // build the method parameters | |||||
| if (methodParameterTypes == null) | |||||
| { | |||||
| ilOfMethod.Emit(OpCodes.Ldnull); | |||||
| } | |||||
| else | |||||
| { | |||||
| var parameters = ilOfMethod.DeclareLocal(typeof(object[])); | |||||
| ilOfMethod.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length); | |||||
| ilOfMethod.Emit(OpCodes.Newarr, typeof(object)); | |||||
| ilOfMethod.Emit(OpCodes.Stloc, parameters); | |||||
| for (var j = 0; j < methodParameterTypes.Length; j++) | |||||
| { | |||||
| ilOfMethod.Emit(OpCodes.Ldloc, parameters); | |||||
| ilOfMethod.Emit(OpCodes.Ldc_I4, j); | |||||
| ilOfMethod.Emit(OpCodes.Ldarg, j + 1); | |||||
| ilOfMethod.Emit(OpCodes.Stelem_Ref); | |||||
| } | |||||
| ilOfMethod.Emit(OpCodes.Ldloc, parameters); | |||||
| } | |||||
| // call Invoke() method of Interceptor | |||||
| ilOfMethod.Emit(OpCodes.Callvirt, typeof(Interceptor).GetMethod("Invoke")); | |||||
| // pop the stack if return void | |||||
| if (method.ReturnType == typeof(void)) | |||||
| { | |||||
| ilOfMethod.Emit(OpCodes.Pop); | |||||
| } | |||||
| // complete | |||||
| ilOfMethod.Emit(OpCodes.Ret); | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| using System.Reflection; | |||||
| using System.Runtime.CompilerServices; | |||||
| using System.Runtime.InteropServices; | |||||
| // General Information about an assembly is controlled through the following | |||||
| // set of attributes. Change these attribute values to modify the information | |||||
| // associated with an assembly. | |||||
| [assembly: AssemblyTitle("EmitCreateDynamicProxy")] | |||||
| [assembly: AssemblyDescription("")] | |||||
| [assembly: AssemblyConfiguration("")] | |||||
| [assembly: AssemblyCompany("")] | |||||
| [assembly: AssemblyProduct("EmitCreateDynamicProxy")] | |||||
| [assembly: AssemblyCopyright("Copyright © 2013")] | |||||
| [assembly: AssemblyTrademark("")] | |||||
| [assembly: AssemblyCulture("")] | |||||
| // Setting ComVisible to false makes the types in this assembly not visible | |||||
| // to COM components. If you need to access a type in this assembly from | |||||
| // COM, set the ComVisible attribute to true on that type. | |||||
| [assembly: ComVisible(false)] | |||||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | |||||
| [assembly: Guid("7dbdf814-4cbd-4fc1-987f-976261539900")] | |||||
| // Version information for an assembly consists of the following four values: | |||||
| // | |||||
| // Major Version | |||||
| // Minor Version | |||||
| // Build Number | |||||
| // Revision | |||||
| // | |||||
| // You can specify all the values or you can default the Build and Revision Numbers | |||||
| // by using the '*' as shown below: | |||||
| // [assembly: AssemblyVersion("1.0.*")] | |||||
| [assembly: AssemblyVersion("1.0.0.0")] | |||||
| [assembly: AssemblyFileVersion("1.0.0.0")] |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
| <PropertyGroup> | |||||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||||
| <Platform Condition=" '$(Platform)' == '' ">x86</Platform> | |||||
| <ProductVersion>8.0.30703</ProductVersion> | |||||
| <SchemaVersion>2.0</SchemaVersion> | |||||
| <ProjectGuid>{C4DFB7F2-8C56-4036-B4FF-E56B456B3943}</ProjectGuid> | |||||
| <OutputType>Exe</OutputType> | |||||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||||
| <RootNamespace>EmitCreateMembers</RootNamespace> | |||||
| <AssemblyName>EmitCreateMembers</AssemblyName> | |||||
| <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | |||||
| <TargetFrameworkProfile>Client</TargetFrameworkProfile> | |||||
| <FileAlignment>512</FileAlignment> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> | |||||
| <PlatformTarget>x86</PlatformTarget> | |||||
| <DebugSymbols>true</DebugSymbols> | |||||
| <DebugType>full</DebugType> | |||||
| <Optimize>false</Optimize> | |||||
| <OutputPath>bin\Debug\</OutputPath> | |||||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> | |||||
| <PlatformTarget>x86</PlatformTarget> | |||||
| <DebugType>pdbonly</DebugType> | |||||
| <Optimize>true</Optimize> | |||||
| <OutputPath>bin\Release\</OutputPath> | |||||
| <DefineConstants>TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <Reference Include="System" /> | |||||
| <Reference Include="System.Core" /> | |||||
| <Reference Include="System.Xml.Linq" /> | |||||
| <Reference Include="System.Data.DataSetExtensions" /> | |||||
| <Reference Include="Microsoft.CSharp" /> | |||||
| <Reference Include="System.Data" /> | |||||
| <Reference Include="System.Xml" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | |||||
| <Compile Include="Program.cs" /> | |||||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||||
| </ItemGroup> | |||||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||||
| Other similar extension points exist, see Microsoft.Common.targets. | |||||
| <Target Name="BeforeBuild"> | |||||
| </Target> | |||||
| <Target Name="AfterBuild"> | |||||
| </Target> | |||||
| --> | |||||
| </Project> |
| using System; | |||||
| using System.Reflection; | |||||
| using System.Reflection.Emit; | |||||
| namespace EmitCreateMembers | |||||
| { | |||||
| class Program | |||||
| { | |||||
| static void Main(string[] args) | |||||
| { | |||||
| // specify a new assembly name | |||||
| var assemblyName = new AssemblyName("Pets"); | |||||
| // create assembly builder | |||||
| var assemblyBuilder = AppDomain.CurrentDomain | |||||
| .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); | |||||
| // create module builder | |||||
| var moduleBuilder = assemblyBuilder.DefineDynamicModule("PetsModule", "Pets.dll"); | |||||
| // create type builder for a class | |||||
| var typeBuilder = moduleBuilder.DefineType("Kitty", TypeAttributes.Public); | |||||
| // then create whole class structure | |||||
| CreateKittyClassStructure(typeBuilder); | |||||
| // then create the whole class type | |||||
| var classType = typeBuilder.CreateType(); | |||||
| // save assembly | |||||
| assemblyBuilder.Save("Pets.dll"); | |||||
| Console.WriteLine("Hi, Dennis, a Pets assembly has been generated for you."); | |||||
| Console.ReadLine(); | |||||
| } | |||||
| private static void CreateKittyClassStructure(TypeBuilder typeBuilder) | |||||
| { | |||||
| // ---- define fields ---- | |||||
| var fieldId = typeBuilder.DefineField( | |||||
| "_id", typeof(int), FieldAttributes.Private); | |||||
| var fieldName = typeBuilder.DefineField( | |||||
| "_name", typeof(string), FieldAttributes.Private); | |||||
| // ---- define costructors ---- | |||||
| Type objType = Type.GetType("System.Object"); | |||||
| ConstructorInfo objCtor = objType.GetConstructor(new Type[0]); | |||||
| Type[] constructorArgs = { typeof(int), typeof(string) }; | |||||
| var constructorBuilder = typeBuilder.DefineConstructor( | |||||
| MethodAttributes.Public, CallingConventions.Standard, constructorArgs); | |||||
| ILGenerator ilOfCtor = constructorBuilder.GetILGenerator(); | |||||
| ilOfCtor.Emit(OpCodes.Ldarg_0); | |||||
| ilOfCtor.Emit(OpCodes.Call, objCtor); | |||||
| ilOfCtor.Emit(OpCodes.Ldarg_0); | |||||
| ilOfCtor.Emit(OpCodes.Ldarg_1); | |||||
| ilOfCtor.Emit(OpCodes.Stfld, fieldId); | |||||
| ilOfCtor.Emit(OpCodes.Ldarg_0); | |||||
| ilOfCtor.Emit(OpCodes.Ldarg_2); | |||||
| ilOfCtor.Emit(OpCodes.Stfld, fieldName); | |||||
| ilOfCtor.Emit(OpCodes.Ret); | |||||
| // ---- define properties ---- | |||||
| var methodGetId = typeBuilder.DefineMethod( | |||||
| "GetId", MethodAttributes.Public, typeof(int), null); | |||||
| var methodSetId = typeBuilder.DefineMethod( | |||||
| "SetId", MethodAttributes.Public, null, new Type[] { typeof(int) }); | |||||
| var ilOfGetId = methodGetId.GetILGenerator(); | |||||
| ilOfGetId.Emit(OpCodes.Ldarg_0); // this | |||||
| ilOfGetId.Emit(OpCodes.Ldfld, fieldId); | |||||
| ilOfGetId.Emit(OpCodes.Ret); | |||||
| var ilOfSetId = methodSetId.GetILGenerator(); | |||||
| ilOfSetId.Emit(OpCodes.Ldarg_0); // this | |||||
| ilOfSetId.Emit(OpCodes.Ldarg_1); // the first one in arguments list | |||||
| ilOfSetId.Emit(OpCodes.Stfld, fieldId); | |||||
| ilOfSetId.Emit(OpCodes.Ret); | |||||
| // create Id property | |||||
| var propertyId = typeBuilder.DefineProperty( | |||||
| "Id", PropertyAttributes.None, typeof(int), null); | |||||
| propertyId.SetGetMethod(methodGetId); | |||||
| propertyId.SetSetMethod(methodSetId); | |||||
| var methodGetName = typeBuilder.DefineMethod( | |||||
| "GetName", MethodAttributes.Public, typeof(string), null); | |||||
| var methodSetName = typeBuilder.DefineMethod( | |||||
| "SetName", MethodAttributes.Public, null, new Type[] { typeof(string) }); | |||||
| var ilOfGetName = methodGetName.GetILGenerator(); | |||||
| ilOfGetName.Emit(OpCodes.Ldarg_0); // this | |||||
| ilOfGetName.Emit(OpCodes.Ldfld, fieldName); | |||||
| ilOfGetName.Emit(OpCodes.Ret); | |||||
| var ilOfSetName = methodSetName.GetILGenerator(); | |||||
| ilOfSetName.Emit(OpCodes.Ldarg_0); // this | |||||
| ilOfSetName.Emit(OpCodes.Ldarg_1); // the first one in arguments list | |||||
| ilOfSetName.Emit(OpCodes.Stfld, fieldName); | |||||
| ilOfSetName.Emit(OpCodes.Ret); | |||||
| // create Name property | |||||
| var propertyName = typeBuilder.DefineProperty( | |||||
| "Name", PropertyAttributes.None, typeof(string), null); | |||||
| propertyName.SetGetMethod(methodGetName); | |||||
| propertyName.SetSetMethod(methodSetName); | |||||
| // ---- define methods ---- | |||||
| // create ToString() method | |||||
| var methodToString = typeBuilder.DefineMethod( | |||||
| "ToString", | |||||
| MethodAttributes.Virtual | MethodAttributes.Public, | |||||
| typeof(string), | |||||
| null); | |||||
| var ilOfToString = methodToString.GetILGenerator(); | |||||
| var local = ilOfToString.DeclareLocal(typeof(string)); // create a local variable | |||||
| ilOfToString.Emit(OpCodes.Ldstr, "Id:[{0}], Name:[{1}]"); | |||||
| ilOfToString.Emit(OpCodes.Ldarg_0); // this | |||||
| ilOfToString.Emit(OpCodes.Ldfld, fieldId); | |||||
| ilOfToString.Emit(OpCodes.Box, typeof(int)); // boxing the value type to object | |||||
| ilOfToString.Emit(OpCodes.Ldarg_0); // this | |||||
| ilOfToString.Emit(OpCodes.Ldfld, fieldName); | |||||
| ilOfToString.Emit(OpCodes.Call, | |||||
| typeof(string).GetMethod("Format", | |||||
| new Type[] { typeof(string), typeof(object), typeof(object) })); | |||||
| ilOfToString.Emit(OpCodes.Stloc, local); // set local variable | |||||
| ilOfToString.Emit(OpCodes.Ldloc, local); // load local variable to stack | |||||
| ilOfToString.Emit(OpCodes.Ret); | |||||
| } | |||||
| } | |||||
| } |
| using System.Reflection; | |||||
| using System.Runtime.CompilerServices; | |||||
| using System.Runtime.InteropServices; | |||||
| // General Information about an assembly is controlled through the following | |||||
| // set of attributes. Change these attribute values to modify the information | |||||
| // associated with an assembly. | |||||
| [assembly: AssemblyTitle("EmitCreateMembers")] | |||||
| [assembly: AssemblyDescription("")] | |||||
| [assembly: AssemblyConfiguration("")] | |||||
| [assembly: AssemblyCompany("")] | |||||
| [assembly: AssemblyProduct("EmitCreateMembers")] | |||||
| [assembly: AssemblyCopyright("Copyright © 2013")] | |||||
| [assembly: AssemblyTrademark("")] | |||||
| [assembly: AssemblyCulture("")] | |||||
| // Setting ComVisible to false makes the types in this assembly not visible | |||||
| // to COM components. If you need to access a type in this assembly from | |||||
| // COM, set the ComVisible attribute to true on that type. | |||||
| [assembly: ComVisible(false)] | |||||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | |||||
| [assembly: Guid("03f6f120-a8c3-4286-98e8-ff62a5812cb0")] | |||||
| // Version information for an assembly consists of the following four values: | |||||
| // | |||||
| // Major Version | |||||
| // Minor Version | |||||
| // Build Number | |||||
| // Revision | |||||
| // | |||||
| // You can specify all the values or you can default the Build and Revision Numbers | |||||
| // by using the '*' as shown below: | |||||
| // [assembly: AssemblyVersion("1.0.*")] | |||||
| [assembly: AssemblyVersion("1.0.0.0")] | |||||
| [assembly: AssemblyFileVersion("1.0.0.0")] |
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||||
| <PropertyGroup> | |||||
| <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||||
| <Platform Condition=" '$(Platform)' == '' ">x86</Platform> | |||||
| <ProductVersion>8.0.30703</ProductVersion> | |||||
| <SchemaVersion>2.0</SchemaVersion> | |||||
| <ProjectGuid>{38396364-FAFD-4D67-B265-32B108A797C0}</ProjectGuid> | |||||
| <OutputType>Exe</OutputType> | |||||
| <AppDesignerFolder>Properties</AppDesignerFolder> | |||||
| <RootNamespace>EmitIntroduction</RootNamespace> | |||||
| <AssemblyName>EmitIntroduction</AssemblyName> | |||||
| <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | |||||
| <TargetFrameworkProfile>Client</TargetFrameworkProfile> | |||||
| <FileAlignment>512</FileAlignment> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> | |||||
| <PlatformTarget>x86</PlatformTarget> | |||||
| <DebugSymbols>true</DebugSymbols> | |||||
| <DebugType>full</DebugType> | |||||
| <Optimize>false</Optimize> | |||||
| <OutputPath>bin\Debug\</OutputPath> | |||||
| <DefineConstants>DEBUG;TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> | |||||
| <PlatformTarget>x86</PlatformTarget> | |||||
| <DebugType>pdbonly</DebugType> | |||||
| <Optimize>true</Optimize> | |||||
| <OutputPath>bin\Release\</OutputPath> | |||||
| <DefineConstants>TRACE</DefineConstants> | |||||
| <ErrorReport>prompt</ErrorReport> | |||||
| <WarningLevel>4</WarningLevel> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <Reference Include="System" /> | |||||
| <Reference Include="System.Core" /> | |||||
| <Reference Include="System.Xml.Linq" /> | |||||
| <Reference Include="System.Data.DataSetExtensions" /> | |||||
| <Reference Include="Microsoft.CSharp" /> | |||||
| <Reference Include="System.Data" /> | |||||
| <Reference Include="System.Xml" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | |||||
| <Compile Include="Program.cs" /> | |||||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||||
| </ItemGroup> | |||||
| <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||||
| <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||||
| Other similar extension points exist, see Microsoft.Common.targets. | |||||
| <Target Name="BeforeBuild"> | |||||
| </Target> | |||||
| <Target Name="AfterBuild"> | |||||
| </Target> | |||||
| --> | |||||
| </Project> |
| using System; | |||||
| using System.Reflection; | |||||
| using System.Reflection.Emit; | |||||
| namespace EmitIntroduction | |||||
| { | |||||
| class Program | |||||
| { | |||||
| static void Main(string[] args) | |||||
| { | |||||
| // specify a new assembly name | |||||
| var assemblyName = new AssemblyName("Kitty"); | |||||
| // create assembly builder | |||||
| var assemblyBuilder = AppDomain.CurrentDomain | |||||
| .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); | |||||
| // create module builder | |||||
| var moduleBuilder = assemblyBuilder.DefineDynamicModule("KittyModule", "Kitty.exe"); | |||||
| // create type builder for a class | |||||
| var typeBuilder = moduleBuilder.DefineType("HelloKittyClass", TypeAttributes.Public); | |||||
| // create method builder | |||||
| var methodBuilder = typeBuilder.DefineMethod( | |||||
| "SayHelloMethod", | |||||
| MethodAttributes.Public | MethodAttributes.Static, | |||||
| null, | |||||
| null); | |||||
| // then get the method il generator | |||||
| var il = methodBuilder.GetILGenerator(); | |||||
| // then create the method function | |||||
| il.Emit(OpCodes.Ldstr, "Hello, Kitty!"); | |||||
| il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); | |||||
| il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine")); | |||||
| il.Emit(OpCodes.Pop); // we just read something here, throw it. | |||||
| il.Emit(OpCodes.Ret); | |||||
| // then create the whole class type | |||||
| var helloKittyClassType = typeBuilder.CreateType(); | |||||
| // set entry point for this assembly | |||||
| assemblyBuilder.SetEntryPoint(helloKittyClassType.GetMethod("SayHelloMethod")); | |||||
| // save assembly | |||||
| assemblyBuilder.Save("Kitty.exe"); | |||||
| Console.WriteLine("Hi, Dennis, a Kitty assembly has been generated for you."); | |||||
| Console.ReadLine(); | |||||
| } | |||||
| } | |||||
| } |
| using System.Reflection; | |||||
| using System.Runtime.CompilerServices; | |||||
| using System.Runtime.InteropServices; | |||||
| // General Information about an assembly is controlled through the following | |||||
| // set of attributes. Change these attribute values to modify the information | |||||
| // associated with an assembly. | |||||
| [assembly: AssemblyTitle("EmitIntroduction")] | |||||
| [assembly: AssemblyDescription("")] | |||||
| [assembly: AssemblyConfiguration("")] | |||||
| [assembly: AssemblyCompany("")] | |||||
| [assembly: AssemblyProduct("EmitIntroduction")] | |||||
| [assembly: AssemblyCopyright("Copyright © 2013")] | |||||
| [assembly: AssemblyTrademark("")] | |||||
| [assembly: AssemblyCulture("")] | |||||
| // Setting ComVisible to false makes the types in this assembly not visible | |||||
| // to COM components. If you need to access a type in this assembly from | |||||
| // COM, set the ComVisible attribute to true on that type. | |||||
| [assembly: ComVisible(false)] | |||||
| // The following GUID is for the ID of the typelib if this project is exposed to COM | |||||
| [assembly: Guid("9ecfd944-391f-44be-8ba2-504d10c2c2e4")] | |||||
| // Version information for an assembly consists of the following four values: | |||||
| // | |||||
| // Major Version | |||||
| // Minor Version | |||||
| // Build Number | |||||
| // Revision | |||||
| // | |||||
| // You can specify all the values or you can default the Build and Revision Numbers | |||||
| // by using the '*' as shown below: | |||||
| // [assembly: AssemblyVersion("1.0.*")] | |||||
| [assembly: AssemblyVersion("1.0.0.0")] | |||||
| [assembly: AssemblyFileVersion("1.0.0.0")] |
| | |||||
| Microsoft Visual Studio Solution File, Format Version 11.00 | |||||
| # Visual Studio 2010 | |||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmitIntroduction", "EmitIntroduction\EmitIntroduction.csproj", "{38396364-FAFD-4D67-B265-32B108A797C0}" | |||||
| EndProject | |||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmitCreateMembers", "EmitCreateMembers\EmitCreateMembers.csproj", "{C4DFB7F2-8C56-4036-B4FF-E56B456B3943}" | |||||
| EndProject | |||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmitCreateDynamicProxy", "EmitCreateDynamicProxy\EmitCreateDynamicProxy.csproj", "{BDA6268A-ADF2-4DA3-8053-81CA4AD007B0}" | |||||
| EndProject | |||||
| Global | |||||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||||
| Debug|x86 = Debug|x86 | |||||
| Release|x86 = Release|x86 | |||||
| EndGlobalSection | |||||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||||
| {38396364-FAFD-4D67-B265-32B108A797C0}.Debug|x86.ActiveCfg = Debug|x86 | |||||
| {38396364-FAFD-4D67-B265-32B108A797C0}.Debug|x86.Build.0 = Debug|x86 | |||||
| {38396364-FAFD-4D67-B265-32B108A797C0}.Release|x86.ActiveCfg = Release|x86 | |||||
| {38396364-FAFD-4D67-B265-32B108A797C0}.Release|x86.Build.0 = Release|x86 | |||||
| {C4DFB7F2-8C56-4036-B4FF-E56B456B3943}.Debug|x86.ActiveCfg = Debug|x86 | |||||
| {C4DFB7F2-8C56-4036-B4FF-E56B456B3943}.Debug|x86.Build.0 = Debug|x86 | |||||
| {C4DFB7F2-8C56-4036-B4FF-E56B456B3943}.Release|x86.ActiveCfg = Release|x86 | |||||
| {C4DFB7F2-8C56-4036-B4FF-E56B456B3943}.Release|x86.Build.0 = Release|x86 | |||||
| {BDA6268A-ADF2-4DA3-8053-81CA4AD007B0}.Debug|x86.ActiveCfg = Debug|x86 | |||||
| {BDA6268A-ADF2-4DA3-8053-81CA4AD007B0}.Debug|x86.Build.0 = Debug|x86 | |||||
| {BDA6268A-ADF2-4DA3-8053-81CA4AD007B0}.Release|x86.ActiveCfg = Release|x86 | |||||
| {BDA6268A-ADF2-4DA3-8053-81CA4AD007B0}.Release|x86.Build.0 = Release|x86 | |||||
| EndGlobalSection | |||||
| GlobalSection(SolutionProperties) = preSolution | |||||
| HideSolutionNode = FALSE | |||||
| EndGlobalSection | |||||
| EndGlobal |