注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

信息 灵感 创新

I? =Information,Inspiration,Innovation

 
 
 

日志

 
 
关于我

we are 5. Mathematics, Computation, Programming, Engineering, and Making fun of life.

网易考拉推荐
GACHA精选

小试牛刀:System.Reflection.Emit  

2011-12-24 16:11:12|  分类: MSIL |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

关于Emit命名空间的说明,MSDN上是这样说的:System.Reflection.Emit命名空间包含允许编译器或工具发出元数据和 Microsoft 中间语言 (MSIL) 并可选择在磁盘上生成PE文件的类。这些类的主要客户端是脚本引擎和编译器。这就提供了一个很重要的信息,可以通过该类来生成PE文件。
Emit命名空间下有许多类,涉及到Assembly、Module、Type、Member、Field、Enum、Event、Attribute、Property、Parameter等的构建,并且还包含OpCodes类提供中间语言指令的字段表示形式,最终可以保存为.dll或者exe(又可区分为CUI或者GUI)文件。
在使用Emit类之前,需要对IL语言有一定的了解,因为Emit正是通过对OpCodes(IL指令)的发出,实现程序集的生成工作,下面是一个演示程序:

using System;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;

namespace EmitDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            MakeAnAssembly();
            Console.Write("\"Emited.exe\" Created");
            Console.Read();
        }

        static void MakeAnAssembly()
        {
            AppDomain domain = Thread.GetDomain();
           
            AssemblyName an = new AssemblyName();
            an.Name = "Emited";
            AssemblyBuilder ab = domain.DefineDynamicAssembly(an,AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder mb = ab.DefineDynamicModule("TheModule", "Emited.exe");
            TypeBuilder tb = mb.DefineType("EmitedClass",TypeAttributes.Public);
            MethodInfo writeLine = typeof(System.Console).GetMethod("WriteLine",new Type[]{typeof(string)});
            MethodInfo read = typeof(System.Console).GetMethod("Read");
            MethodBuilder entry = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static,CallingConventions.Standard);
            ILGenerator main = entry.GetILGenerator();
            main.Emit(OpCodes.Ldstr, "Greetings From Main");
            main.Emit(OpCodes.Call, writeLine);
            main.Emit(OpCodes.Call, read);
            main.Emit(OpCodes.Pop);//Read会带来入栈,所以要Pop保证堆栈平衡
            main.Emit(OpCodes.Ret);
            tb.CreateType();
            ab.SetEntryPoint(entry, PEFileKinds.ConsoleApplication);
            ab.Save("Emited.exe");
        }
    }
}

程序的说明:通过AssemblyBuilder创建一个Assembly,然后是模块,接下来是类型,然后是类型中的方法,最后是方法体的生成,方法体通过ILGenerator.Emit方法给方法体添加Opcodes,完成对方法体的调用。最后保存程序集。
程序运行结果是释放一个文件,文件名称为Emited.exe,该文件中只有一个Main方法,其实方法名称并不重要,重要的是要将SetEntryPoint设置到一个方法上,该方法就相当于C#中的Main方法,为整个程序的入口,如果释放的是DLL文件,则不需要设置该方法。
运行一下Emited.exe文件:

小试牛刀:System.Reflection.Emit - Castor - 趁年轻,多折腾

按回车程序退出。

使用Reflector看看我们释放的文件:

小试牛刀:System.Reflection.Emit - Castor - 趁年轻,多折腾

 IL代码

小试牛刀:System.Reflection.Emit - Castor - 趁年轻,多折腾

 C#代码

C#代码没什么好看的了,主要说说IL代码的分析,.entrypoint就是程序中使用的SetEntryPoint的结果,Emit IL代码的时候,WriteLine由于被重载了,所以要指明调用的参数列表,这里当然是字符串类型,然后又调用了Read方法,注意Read之后CLR认为会有一个值入栈,所以需要pop一下保证堆栈的平衡,才能让返回程序,比较有意思的是.maxstack属性,由于没有在释放代码中指明,于是使用了默认的值1,通常这个大小是不够的,C#编译器给方法中默认的值是8,不过不知道如何在Emit中修改。

最后,程序写完才发现,Emit变为过去式或者被动语态的时候应该是Emitted,而不是Emited,写程序的时候根本没想那么多,同时也反映了自己英语问题很严重了……

最后的最后,祝大家圣诞、元旦 双蛋快乐。


 


 

  评论这张
 
阅读(2010)| 评论(2)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2016