最基本的动态编译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)网站