最基本的動態編譯css
.Net爲咱們提供了很強大的支持來實現這一切咱們能夠去作的基礎,主要應用的兩個命名空間是:System.CodeDom.Compiler和Microsoft.CSharp或Microsoft.VisualBasic。另外還須要用到反射來動態執行你的代碼。動態編譯並執行代碼的原理其實在於將提供的源代碼交予CSharpCodeProvider來執行編譯(其實和CSC沒什麼兩樣),若是沒有任何編譯錯誤,生成的IL代碼會被編譯成DLL存放于于內存並加載在某個應用程序域(默認爲當前)內並經過反射的方式來調用其某個方法或者觸發某個事件等。之因此說它是插件編寫的一種方式也正是由於與此,咱們能夠經過預先定義好的藉口來組織和擴展咱們的程序並將其交還給主程序去觸發。一個基本的動態編譯並執行代碼的步驟包括:如下代碼片斷包含了完整的編譯和執行過程:html
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Microsoft.CSharp; using System.CodeDom.Compiler; using System.Reflection; namespace DynamicCompileBase { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //get the code to compile string strSourceCode = this.txtSource.Text; // 1.Create a new CSharpCodePrivoder instance CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider(); // 2.Sets the runtime compiling parameters by crating a new CompilerParameters instance CompilerParameters objCompilerParameters = new CompilerParameters(); objCompilerParameters.ReferencedAssemblies.Add("System.dll"); objCompilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll"); objCompilerParameters.GenerateInMemory = true; // 3.CompilerResults: Complile the code snippet by calling a method from the provider CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(objCompilerParameters, strSourceCode); if (cr.Errors.HasErrors) { string strErrorMsg = cr.Errors.Count.ToString() + " Errors:"; for (int x = 0; x < cr.Errors.Count; x++) { strErrorMsg = strErrorMsg + "/r/nLine: " + cr.Errors[x].Line.ToString() + " - " + cr.Errors[x].ErrorText; } this.txtResult.Text = strErrorMsg; MessageBox.Show("There were build erros, please modify your code.", "Compiling Error"); return; } // 4. Invoke the method by using Reflection Assembly objAssembly = cr.CompiledAssembly; object objClass = objAssembly.CreateInstance("Dynamicly.HelloWorld"); if (objClass == null) { this.txtResult.Text = "Error: " + "Couldn't load class."; return; } object[] objCodeParms = new object[1]; objCodeParms[0] = "Allan."; string strResult = (string)objClass.GetType().InvokeMember( "GetTime", BindingFlags.InvokeMethod, null, objClass, objCodeParms); this.txtResult.Text = strResult; } } }須要解釋的是,這裏咱們在傳遞編譯參數時設置了GenerateInMemory爲true,這代表生成的DLL會被加載在內存中(隨後被默認引用入當前應用程序域)。在調用GetTime方法時咱們須要加入參數,傳遞object類型的數組並經過Reflection的InvokeMember來調用。在建立生成的Assembly中的對象實例時,須要注意用到的命名空間是你輸入代碼的真實命名空間。如下是咱們輸入的測試代碼(爲了方便,全部的代碼都在外部輸入,動態執行時不作調整):
using System; namespace Dynamicly { public class HelloWorld { public string GetTime(string strName) { return "Welcome " + strName + ", Check in at " + System.DateTime.Now.ToString(); } } }
運行附件中提供的程序,能夠很容易獲得如下結果:
數組
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace RemoteAccess { /// <summary> /// Interface that can be run over the remote AppDomain boundary. /// </summary> public interface IRemoteInterface { object Invoke(string lcMethod, object[] Parameters); } /// <summary> /// Factory class to create objects exposing IRemoteInterface /// </summary> public class RemoteLoaderFactory : MarshalByRefObject { private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance; public RemoteLoaderFactory() { } public IRemoteInterface Create(string assemblyFile, string typeName, object[] constructArgs) { return (IRemoteInterface)Activator.CreateInstanceFrom( assemblyFile, typeName, false, bfi, null, constructArgs, null, null, null).Unwrap(); } } }接下來在原來基礎上須要修改的是:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Microsoft.CSharp; using System.CodeDom.Compiler; using System.Reflection; using RemoteAccess; namespace DynamicCompileAppDomain { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { // get the code to compile string strSourceCode = this.txtSource.Text; // 0. Create an addtional AppDomain AppDomainSetup objSetup = new AppDomainSetup(); objSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; AppDomain objAppDomain = AppDomain.CreateDomain("MyAppDomain", null, objSetup); // 1.Create a new CSharpCodePrivoder instance CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider(); // 2.Sets the runtime compiling parameters by crating a new CompilerParameters instance CompilerParameters objCompilerParameters = new CompilerParameters(); objCompilerParameters.ReferencedAssemblies.Add("System.dll"); objCompilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll"); // Load the remote loader interface objCompilerParameters.ReferencedAssemblies.Add("RemoteAccess.dll"); // Load the resulting assembly into memory objCompilerParameters.GenerateInMemory = false; objCompilerParameters.OutputAssembly = "DynamicalCode.dll"; // 3.CompilerResults: Complile the code snippet by calling a method from the provider CompilerResults cr = objCSharpCodePrivoder.CompileAssemblyFromSource(objCompilerParameters, strSourceCode); if (cr.Errors.HasErrors) { string strErrorMsg = cr.Errors.Count.ToString() + " Errors:"; for (int x = 0; x < cr.Errors.Count; x++) { strErrorMsg = strErrorMsg + "/r/nLine: " + cr.Errors[x].Line.ToString() + " - " + cr.Errors[x].ErrorText; } this.txtResult.Text = strErrorMsg; MessageBox.Show("There were build erros, please modify your code.", "Compiling Error"); return; } // 4. Invoke the method by using Reflection RemoteLoaderFactory factory = (RemoteLoaderFactory)objAppDomain.CreateInstance("RemoteAccess", "RemoteAccess.RemoteLoaderFactory").Unwrap(); // with help of factory, create a real 'LiveClass' instance object objObject = factory.Create("DynamicalCode.dll", "Dynamicly.HelloWorld", null); if (objObject == null) { this.txtResult.Text = "Error: " + "Couldn't load class."; return; } // *** Cast object to remote interface, avoid loading type info IRemoteInterface objRemote = (IRemoteInterface)objObject; object[] objCodeParms = new object[1]; objCodeParms[0] = "Allan."; string strResult = (string)objRemote.Invoke("GetTime", objCodeParms); this.txtResult.Text = strResult; //Dispose the objects and unload the generated DLLs. objRemote = null; AppDomain.Unload(objAppDomain); System.IO.File.Delete("DynamicalCode.dll"); } } }對於客戶端的輸入程序,咱們須要繼承於MarshalByRefObject類和IRemoteInterface接口,並添加對RemoteAccess程序集的引用。如下爲輸入:
using System; using System.Reflection; using RemoteAccess; namespace Dynamicly { public class HelloWorld : MarshalByRefObject,IRemoteInterface { public object Invoke(string strMethod,object[] Parameters) { return this.GetType().InvokeMember(strMethod, BindingFlags.InvokeMethod,null,this,Parameters); } public string GetTime(string strName) { return "Welcome " + strName + ", Check in at " + System.DateTime.Now.ToString(); } } }這樣,你能夠經過適時的編譯,加載和卸載程序集來保證你的程序始終處於一個可控消耗的過程,而且達到了動態編譯的目的,並且由於在不一樣的應用程序域中,讓你的自己的程序更加安全和健壯。
最後附上示例程序源代碼:安全
http://pan.baidu.com/s/1skSPQ3b
服務器
示例程序共有4個項目:ide
DynamicCompile是http://blog.csdn.net/clb929/article/details/51371363這篇文章的練習程序工具
DynamicCompileBase是本文無應用程序域動態編譯的例子(沒法釋放內存)
性能
RemoteAccess是遠程調用應用程序域的庫
測試
DynamicCompileAppDomain是遠程調用應用程序域動態編譯的示例程序(可以將C#代碼編譯爲臨時DLL,動態加載並執行,而後釋放,最後刪除臨時DLL)網站