标签:end data name for job ide ast nload real
原文:让C#语言充当自身脚本!——.NET中的动态编译
最基本的动态编译
.Net为我们提供了很强大的支持来实现这一切我们可以去做的基础,主要应用的两个命名空间是:System.CodeDom.Compiler和Microsoft.CSharp或Microsoft.VisualBasic。另外还需要用到反射来动态执行你的代码。动态编译并执行代码的原理其实在于将提供的源代码交予CSharpCodeProvider来执行编译(其实和CSC没什么两样),如果没有任何编译错误,生成的IL代码会被编译成DLL存放于于内存并加载在某个应用程序域(默认为当前)内并通过反射的方式来调用其某个方法或者触发某个事件等。之所以说它是插件编写的一种方式也正是因为与此,我们可以通过预先定义好的借口来组织和扩展我们的程序并将其交还给主程序去触发。一个基本的动态编译并执行代码的步骤包括:以下代码片段包含了完整的编译和执行过程:
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个项目:
DynamicCompile是http://blog.csdn.net/clb929/article/details/51371363这篇文章的练习程序
DynamicCompileBase是本文无应用程序域动态编译的例子(无法释放内存)
RemoteAccess是远程调用应用程序域的库
DynamicCompileAppDomain是远程调用应用程序域动态编译的示例程序(能够将C#代码编译为临时DLL,动态加载并执行,然后释放,最后删除临时DLL)
标签:end data name for job ide ast nload real
原文地址:https://www.cnblogs.com/lonelyxmas/p/10226905.html