close

自從知道 PostSharp 這套有趣的 AOP 元件後,就一直對其中的原理感到好奇。根據官方部落格,其中所使用的一項技術,就是 IL Injection,所以我嚐試尋找可行的作法。

.Net Framework 其實有提供相關的功能(ILGenerator),可參考下列網址:

http://msdn.microsoft.com/zh-tw/library/system.reflection.emit(v=vs.95).aspx

但使用 ILGenerator 會有些不方便,因為得對 IL 有一定的了解才有辦法實作。於是乎我開始尋找相關的工作來用,之後找到了 Cecil。它是 Mono 底下的專案,將一些 Reflection 相關的 API 包裝起來,比直接使用 Reflection API 方便不少。

 

而底下的範例,將會示範如何將某個類別底下的方法塞進另一個類別底下的方法,首先宣告 TestClass 以及 TestClass2。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestClassLibrary
{
    public class TestClass
    {
        public string Field1 { get; set; }
        public string Field2 { get; set; }

        public void TestMethod()
        {
            string result = string.Empty;
            result = this.GetType().Name;
            Console.WriteLine(result);
        }

    }

    public class TestClass2
    {
        public void TestMethod1()
        {
            Console.WriteLine("1");
            Console.WriteLine("2");
            Console.WriteLine("3");
        }
    }
}

 

接下來將 TestClass2 底下的 TestMethod1 插進 TestClass 底下的 TestMethod。

            string pathBin = "TestClassLibrary.dll";
            AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(pathBin);

            TypeDefinition TestClassType = assembly.MainModule.GetType("TestClassLibrary.TestClass");
            TypeDefinition TestClassType2 = assembly.MainModule.GetType("TestClassLibrary.TestClass2");
            MethodDefinition method1 = TestClassType.Methods.Where(m => m.Name == "TestMethod").First();
            MethodDefinition method2 = TestClassType2.Methods.Where(m => m.Name == "TestMethod1").First();
            ILProcessor ilProcessor = method1.Body.GetILProcessor();
            Instruction inst = method1.Body.Instructions[0];
           
            foreach (Instruction instruction in method2.Body.Instructions)
            {
                ilProcessor.Append(instruction);
            }

            assembly.Write("TestClassLibrary.dll");

 

之後我另開一個專案,引用 TestClassLibray.dll 元件,但 TestMethod 的執行結果沒任何改變,於是我直接檢視 IL code,發現我忽略一個很重要的事情…

2013-06-18_232719

ret 指令就相當於原始碼裡的 return ,這東西插在原始碼裡,後面的邏輯當然都不會執行到。之後就改變一下程式的寫法。

            foreach (Instruction instruction in method2.Body.Instructions)
            {
                if (instruction.OpCode.Code != Code.Ret)
                    ilProcessor.InsertBefore(inst, instruction);
            }

避開 ret 指令後,就達到我要的效果了。

 

參考資料

http://www.mono-project.com/Cecil:FAQ

Cecil 下載位址

https://github.com/jbevain/cecil

arrow
arrow
    全站熱搜

    卑微研究生 發表在 痞客邦 留言(0) 人氣()