有時候我們需要嘗試動態地與一些代碼進行交互,而不是只能執行程式內已編死的代碼,那該怎麼辦呢?我首先推薦各種腳本語言,如Javascript、Lua、Python等等,這些腳本語言有很多優秀的第三方類庫,可以很方便的與 .NET 系統集成,讓我們的程式中執行動態代碼。 但如果你一定想用VB.NET或者 ...
有時候我們需要嘗試動態地與一些代碼進行交互,而不是只能執行程式內已編死的代碼,那該怎麼辦呢?
我首先推薦各種腳本語言,如Javascript、Lua、Python等等,這些腳本語言有很多優秀的第三方類庫,可以很方便的與 .NET 系統集成,讓我們的程式中執行動態代碼。
但如果你一定想用VB.NET或者C#的代碼來運行一段程式,這裡就要用到動態編譯的功能了。
下麵是我寫的兩個實例,你只需要在窗體 FormMain
中添加一個 button
和一個 textbox
即可,預設名為 Button1
、TextBox1
。
VB.NET代碼
Imports System.CodeDom.Compiler Imports System.Reflection Public Class FormMain Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ' 編譯參數 Dim cpars As New CompilerParameters ' 編譯參數,如 /optimize /removeintchecks 等 cpars.CompilerOptions = "/optimize " cpars.GenerateInMemory = True '在記憶體中編譯而不輸出文件 cpars.GenerateExecutable = False '並不輸出執行文件 cpars.IncludeDebugInformation = False '不需要調試信息 ' 導入類庫(根據自己代碼的需要導入) cpars.ReferencedAssemblies.Add("mscorlib.dll") cpars.ReferencedAssemblies.Add("System.dll") cpars.ReferencedAssemblies.Add("System.Data.dll") cpars.ReferencedAssemblies.Add("System.Deployment.dll") cpars.ReferencedAssemblies.Add("System.Drawing.dll") cpars.ReferencedAssemblies.Add("System.Windows.Forms.dll") cpars.ReferencedAssemblies.Add("System.Xml.dll") cpars.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll") ' 編譯參數,為導入的類庫設置全局引用(否則必須使用完整的命名空間名稱才能正確調用函數) cpars.CompilerOptions &= " /imports:" & _ "Microsoft.VisualBasic," & _ "System," & _ "System.Collections," & _ "System.Collections.Generic," & _ "System.Drawing," & _ "System.Windows.Forms" ' 設置編譯器 Dim vbc As New VBCodeProvider 'Dim vbc = CodeDomProvider.CreateProvider("VisualBasic") '等效方法 ' 一個簡單的模板類 Dim codex As String = _ "Public Class CompClass" & vbCrLf & _ " Shared Function RunCode() As Object" & vbCrLf & _ " '$" & vbCrLf & _ " End Function" & vbCrLf & _ "End Class" ' 替換代碼到模板類中 Dim code As String = codex.Replace("'$", TextBox1.Text) ' 編譯並返回 Dim resut As CompilerResults = vbc.CompileAssemblyFromSource(cpars, code) ' 如果發生了錯誤 If resut.Errors.Count > 0 Then MsgBox(resut.Errors(0).ToString) Return End If ' 嘗試調用代碼 Dim asm As Assembly = resut.CompiledAssembly '獲取程式集 ' 獲取我們編寫的靜態方法 Dim mi As MethodInfo = asm.GetType("CompClass").GetMethod("RunCode") ' 執行代碼,並獲取返回值 Dim ret As Object = mi.Invoke(Nothing, Nothing) ' 對返回值進行處理 If ret IsNot Nothing Then MsgBox(ret.ToString) End If End Sub End Class
執行程式,在 Textbox1 里寫入一些VB代碼,按 Button1 即可立即執行裡面的代碼。
如果擁有返回值,程式還可以獲取代碼的返回值,但有可能需要你進行拆箱處理。
C#代碼
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; using System.CodeDom.Compiler; namespace WindowsFormsApplication1 { public partial class FormMain : Form { public FormMain() { InitializeComponent(); } private void Button1_Click(object sender, EventArgs e) { // 編譯參數 var cpars = new CompilerParameters(); cpars.CompilerOptions = "/optimize "; cpars.GenerateInMemory = true; cpars.GenerateExecutable = false; cpars.IncludeDebugInformation = false; // 導入類庫(根據自己代碼的需要導入) cpars.ReferencedAssemblies.Add("mscorlib.dll"); cpars.ReferencedAssemblies.Add("System.dll"); cpars.ReferencedAssemblies.Add("System.Data.dll"); cpars.ReferencedAssemblies.Add("System.Deployment.dll"); cpars.ReferencedAssemblies.Add("System.Drawing.dll"); cpars.ReferencedAssemblies.Add("System.Windows.Forms.dll"); cpars.ReferencedAssemblies.Add("System.Xml.dll"); // 編譯器實例 var csc = new Microsoft.CSharp.CSharpCodeProvider(); //var csc = CodeDomProvider.CreateProvider("CSharp"); // 一個簡單的模板類 // 因為C#的編譯器無法設置全局命名空間,所以需要在代碼中導入命名空間 var codex = @" using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Windows.Forms; class CompClass{ static public object RunCode(){ //$ return null; } } "; // 替換代碼到模板類中 var code = codex.Replace("//$", TextBox1.Text); // 編譯並返回 var resut = csc.CompileAssemblyFromSource(cpars, code); // 錯誤警告 if (resut.Errors.Count > 0) { MessageBox.Show(resut.Errors[0].ToString()); return; } // 調用代碼 var asm = resut.CompiledAssembly; var mi = asm.GetType("CompClass").GetMethod("RunCode"); object ret = mi.Invoke(null, null); if (ret != null) { MessageBox.Show(ret.ToString()); } } } }
C#的代碼流程與VB的基本相同,區別在於C#的編譯器沒有導入全局命名空間的參數,因此需要在模板類里寫入你需要導入的命名空間。
其他的用法基本都一樣。
PS: 我有空再寫一點與第三方腳本庫進行交互的代碼示例。