颖风 發表於 2020-1-14 10:56:00

c# Emit技术解析

<p>我们常常有一个应用场景,由我们的C#代码,动态生成一个EXE,其应用场景可以非常多,比如软件授权,可以输入授权信息后,生成一个授权的DLL等,那如何实现这个功能呢,就要提到一个技术Emit。</p>
<p>&nbsp;</p>
<p>1、Emit概述</p>
<p>Emit,可以称为发出或者产生。在Framework中,与Emit相关的类基本都存在于System.Reflection.Emit命名<br>空间下。可见Emit是作为反射的一个元素存在的。说道反射,大家应该都不陌生,它允许我们查看程序集的元素据,从而取得形如程序集包含哪些类型,类型包<br>含哪些方法等等大量的信息。但是反射也仅能够‘看’,而Emit则可以在运行时动态生成代码。接下来就来看看如何用Emit生成代码。</p>
<p>2、程序集(Assembly)和模块(Managed Module)</p>
<p>程序集是一个或多个模块、资源文件的逻辑性分组,其次程序集是重用,安全性和版本控制的最小单元。我们所见到的DLL、EXE都可以称为一个Assembly,一个Assembly里面包含多个Module,不过通常,我们VS编译的时候,会只编译一个Module,假如在一个Assembly中要编译多个Module,则要借助csc.exe实现。</p>
<p>3、动态生成代码操作</p>
<ul>
<li>定义程序集</li>






</ul>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个程序集的名称</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> asmName = <span style="color: rgba(0, 0, 255, 1)">new</span> AssemblyName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyClass</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">首先就需要定义一个程序集</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);</pre>
</div>
<ul>
<li>定义模块,和指定程序集的保存名称</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个构建类</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> defModuleBuilder = defAssembly.DefineDynamicModule(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyModule</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyAssembly.dll</span><span style="color: rgba(128, 0, 0, 1)">"</span>);</pre>
</div>
<ul>
<li>定义一个类 和方法</li>
</ul>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个类</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> defClassBuilder =defModuleBuilder.DefineType(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyClass</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, TypeAttributes.Public);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个方法</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> methodBldr = defClassBuilder.DefineMethod(<span style="color: rgba(128, 0, 0, 1)">"My</span><span style="color: rgba(128, 0, 0, 1)">Method</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
    MethodAttributes.Public,
      </span><span style="color: rgba(0, 0, 255, 1)">null</span>,<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回类型</span>
      <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">参数的类型</span>
);</pre>
</div>
<p>以上通过创建,已经确定了程序集和模块,也定义了当前模块中的一个类和方法,但这个类的MyMethod方法只定义了一个声明,并没有定义实体操作,以下就需要应用到Emit技术中一个技术OpCode。</p>
<p>OpCode 是描述中间语言 (IL) 指令。这个指令非常多,可以查看微软官网:https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.opcodes?view=netframework-4.8</p>
<p>通过OpCode我们可以定义方法的内容如下:</p>
<div class="cnblogs_code">
<pre>            <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取IL生成器</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> il =<span style="color: rgba(0, 0, 0, 1)"> defMethodBuilder.GetILGenerator();
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个字符串</span>
            il.Emit(OpCodes.Ldstr, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">生成的第一个程序</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用一个函数</span>
            il.Emit(OpCodes.Call, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(Console).GetMethod(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">WriteLine</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">new</span> Type[] { <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) }));
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回到方法开始(返回)</span>
            il.Emit(OpCodes.Ret);</pre>
</div>
<p>通过以上的定义,我们完成了一个程序集、模块、类和方法的定义,我们怎么把以上的定义的信息进行创建和保存,需要调用以下函数:</p>
<div class="cnblogs_code">
<pre>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建类型</span>
<span style="color: rgba(0, 0, 0, 1)">            defClassBuilder.CreateType();

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">保存程序集</span>
            defAssembly.Save(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyAssemblydll</span><span style="color: rgba(128, 0, 0, 1)">"</span>);</pre>
</div>
<p>我们可以在运行程序看到如下效果:</p>
<p><img src="https://img2018.cnblogs.com/blog/24761/202001/24761-20200114125743406-1587844194.png"></p>
<p>&nbsp;</p>
<p>&nbsp; 以下通过创建程序集,并且调用的代码如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Collections.Generic;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Linq;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Reflection;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Reflection.Emit;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text;
</span><span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Threading.Tasks;

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> ConsoleApp1
{
    </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
    {
      </span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
      {

            CreateAssembly();
            LoadAssembly();

            Console.ReadKey();
      }

      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> LoadAssembly()
      {
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> ass = AppDomain.CurrentDomain.Load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyAssembly</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> m = ass.GetModule(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyModule</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> ts =<span style="color: rgba(0, 0, 0, 1)"> m.GetTypes();
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> t =<span style="color: rgba(0, 0, 0, 1)"> ts.FirstOrDefault();
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (t != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">object</span> obj =<span style="color: rgba(0, 0, 0, 1)"> Activator.CreateInstance(t);
                </span><span style="color: rgba(0, 0, 255, 1)">var</span> me = t.GetMethod(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyMethod</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
                me.Invoke(obj, </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
            }
      }
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> CreateAssembly()
      {


            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个程序集的名称</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> asmName = <span style="color: rgba(0, 0, 255, 1)">new</span> AssemblyName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyAssembly</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">首先就需要定义一个程序集</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> defAssembly =<span style="color: rgba(0, 0, 0, 1)"> AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个构建类</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> defModuleBuilder = defAssembly.DefineDynamicModule(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyModule</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyAssembly.dll</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);


            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个类</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> defClassBuilder = defModuleBuilder.DefineType(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyClass</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, TypeAttributes.Public);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个方法</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> defMethodBuilder = defClassBuilder.DefineMethod(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyMethod</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
                MethodAttributes.Public,
                </span><span style="color: rgba(0, 0, 255, 1)">null</span>,<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回类型</span>
                <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">参数类型</span>
<span style="color: rgba(0, 0, 0, 1)">                );
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取IL生成器</span>
            <span style="color: rgba(0, 0, 255, 1)">var</span> il =<span style="color: rgba(0, 0, 0, 1)"> defMethodBuilder.GetILGenerator();
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义一个字符串</span>
            il.Emit(OpCodes.Ldstr, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">生成的第一个程序</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用一个函数</span>
            il.Emit(OpCodes.Call, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(Console).GetMethod(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">WriteLine</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">new</span> Type[] { <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) }));
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回到方法开始(返回)</span>
<span style="color: rgba(0, 0, 0, 1)">            il.Emit(OpCodes.Ret);

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建类型</span>
<span style="color: rgba(0, 0, 0, 1)">            defClassBuilder.CreateType();

            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">保存程序集</span>
            defAssembly.Save(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MyAssembly.dll</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      }

    }
}</span></pre>
</div>
<p>显示效果如下:</p>
<p><img src="https://img2018.cnblogs.com/blog/24761/202001/24761-20200114105603902-1432834726.png"></p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/minhost/p/12190865.html
頁: [1]
查看完整版本: c# Emit技术解析