c# Emit技術解析


我們常常有一個應用場景,由我們的C#代碼,動態生成一個EXE,其應用場景可以非常多,比如軟件授權,可以輸入授權信息后,生成一個授權的DLL等,那如何實現這個功能呢,就要提到一個技術Emit。

 

1、Emit概述

Emit,可以稱為發出或者產生。在Framework中,與Emit相關的類基本都存在於System.Reflection.Emit命名
空間下。可見Emit是作為反射的一個元素存在的。說道反射,大家應該都不陌生,它允許我們查看程序集的元素據,從而取得形如程序集包含哪些類型,類型包
含哪些方法等等大量的信息。但是反射也僅能夠‘看’,而Emit則可以在運行時動態生成代碼。接下來就來看看如何用Emit生成代碼。

2、程序集(Assembly)和模塊(Managed Module)

程序集是一個或多個模塊、資源文件的邏輯性分組,其次程序集是重用,安全性和版本控制的最小單元。我們所見到的DLL、EXE都可以稱為一個Assembly,一個Assembly里面包含多個Module,不過通常,我們VS編譯的時候,會只編譯一個Module,假如在一個Assembly中要編譯多個Module,則要借助csc.exe實現。

3、動態生成代碼操作

  • 定義程序集

 

  //定義一個程序集的名稱
 var asmName = new AssemblyName("MyClass");

//首先就需要定義一個程序集
 var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
  • 定義模塊,和指定程序集的保存名稱
//定義一個構建類
var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");
  • 定義一個類 和方法
 //定義一個類
 var defClassBuilder =defModuleBuilder.DefineType("MyClass", TypeAttributes.Public);
 //定義一個方法
var methodBldr = defClassBuilder.DefineMethod("MyMethod",
    MethodAttributes.Public,
      null,//返回類型
      null//參數的類型
 );

以上通過創建,已經確定了程序集和模塊,也定義了當前模塊中的一個類和方法,但這個類的MyMethod方法只定義了一個聲明,並沒有定義實體操作,以下就需要應用到Emit技術中一個技術OpCode。

OpCode 是描述中間語言 (IL) 指令。這個指令非常多,可以查看微軟官網:https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.opcodes?view=netframework-4.8

通過OpCode我們可以定義方法的內容如下:

            //獲取IL生成器
            var il = defMethodBuilder.GetILGenerator();
            //定義一個字符串
            il.Emit(OpCodes.Ldstr, "生成的第一個程序");
            //調用一個函數
            il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            //返回到方法開始(返回)
            il.Emit(OpCodes.Ret);

通過以上的定義,我們完成了一個程序集、模塊、類和方法的定義,我們怎么把以上的定義的信息進行創建和保存,需要調用以下函數:

        //創建類型
            defClassBuilder.CreateType();

            //保存程序集
            defAssembly.Save("MyAssemblydll");

我們可以在運行程序看到如下效果:

 

  以下通過創建程序集,並且調用的代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {

            CreateAssembly();
            LoadAssembly();

            Console.ReadKey();
        }

        public static void LoadAssembly()
        {
            var ass = AppDomain.CurrentDomain.Load("MyAssembly");
            var m = ass.GetModule("MyModule");
            var ts = m.GetTypes();
            var t = ts.FirstOrDefault();
            if (t != null)
            {
                object obj = Activator.CreateInstance(t);
                var me = t.GetMethod("MyMethod");
                me.Invoke(obj, null);
            }
        }
        public static void CreateAssembly()
        {


            //定義一個程序集的名稱
            var asmName = new AssemblyName("MyAssembly");

            //首先就需要定義一個程序集
            var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
            //定義一個構建類
            var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");


            //定義一個類
            var defClassBuilder = defModuleBuilder.DefineType("MyClass", TypeAttributes.Public);

            //定義一個方法
            var defMethodBuilder = defClassBuilder.DefineMethod("MyMethod",
                MethodAttributes.Public,
                null,//返回類型
                null//參數類型
                );
            //獲取IL生成器
            var il = defMethodBuilder.GetILGenerator();
            //定義一個字符串
            il.Emit(OpCodes.Ldstr, "生成的第一個程序");
            //調用一個函數
            il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            //返回到方法開始(返回)
            il.Emit(OpCodes.Ret);

            //創建類型
            defClassBuilder.CreateType();

            //保存程序集
            defAssembly.Save("MyAssembly.dll");
        }

    }
}

顯示效果如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM