程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 使用Cecil修改.Net程序集

使用Cecil修改.Net程序集

編輯:關於.NET

Cecil 是 Mono 的一個子項目,用於對程序集進行讀寫,並且已經用於 Mono 的調試,Reflector 也使用它作為底層庫。最近把 DbEntry 使用 Emit 生成程序集的方式,改成了使用 Cecil 的方式,就我的感受來說,Cecil 是比較優秀的,有一些地方,比 Emit 使用起來還舒服的多;不過,有一些地方也比較繁瑣。

我使用的是 Git 裡的最新版本,如果大家要測試的話,也建議使用 Git 版,所以,需要安裝一個 Git 客戶端。

這裡,用一個非常簡單的例子,說明一下 Cecil 的基本用法。

首先,我們編寫一個測試用的程序集 TestApp.exe :

using System;
namespace TestApp
{
   class Program
   {
     static void Main()
     {
       Console.WriteLine("Main");
     }
     private static void Before()
     {
       Console.WriteLine("Before");
     }
     private static void After()
     {
       Console.WriteLine("After");
     }
   }
}

然後,編寫一個使用 Cecil 進行改寫的應用 CecilTest.exe :

using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace CecilTest
{
   class Program
   {
     static void Main(string[] args)
     {
       if(args.Length != 1)
       {
         Console.WriteLine("Usage: CecilTest TestApp.exe");
       }
       var m = ModuleDefinition.ReadModule(args[0]);
       var prog = m.Types.First(p => p.Name == "Program");
       var main = prog.Methods.First(p => p.Name == "Main");
       var before = prog.Methods.First(p => p.Name == "Before");
       var after = prog.Methods.First(p => p.Name == "After");
       var il = main.Body.GetILProcessor();
       il.InsertBefore(main.Body.Instructions[0], il.Create(OpCodes.Call, before));
       il.InsertBefore(main.Body.Instructions.Last(), il.Create(OpCodes.Call, after));
       m.Write(args[0] + ".exe");
       Console.WriteLine("Done");
       Console.ReadLine();
     }
   }
}

編譯這兩個項目,並且使用 CecilTest.exe 處理一下 TestApp.exe,生成 TestApp.exe.exe,運行 TestApp.exe,運行結果:

Main

運行 TestApp.exe.exe,運行結果:

Before

Main

After

可以看到,我們已經成功的在 Main 函數的前後,分別插入了一次函數調用。

基本使用方法就是這樣,大體和 Emit 類似,只是不止可以使用 Emit 函數,還可以直接修改程序集。當然,還有其它一些細節的不同,比如它的變量定義不是通過 ILProcessor,而是直接操作函數體的 Variables;再比如它沒有 DeclareLabel 函數,跳轉直接引用函數體的 Instruction 進行。另外,它可以只加載目標程序集,卻不加載其依賴項,所以很多東西都分定義和引用兩種。

就目前來說,我認為它的效果是令人滿意的。不過它最大的問題,在於泛型處理上。不是說不能做,而是太繁瑣,有時候甚至是不可能。在 DbEntry 中,最後被泛型擊敗,采取了 Reflection+Cecil 的方式,這種方式簡單易行,不過問題是,Reflection 需要加載程序集,除了可能出現無法加載依賴項的異常外,也無法簡單的回寫原文件。我在 Cecil 的 Git 上提交了 issue,不過作者回復,不覺得這是問題,所以我也懶得糾纏了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved