動態執行 VB.NET 和 C# 代碼

有時候咱們須要嘗試動態地與一些代碼進行交互,而不是隻能執行程序內已編死的代碼,那該怎麼辦呢?
我首先推薦各類腳本語言,如Javascript、Lua、Python等等,這些腳本語言有不少優秀的第三方類庫,能夠很方便的與 .NET 系統集成,讓咱們的程序中執行動態代碼。ide

但若是你必定想用VB.NET或者C#的代碼來運行一段程序,這裏就要用到動態編譯的功能了。函數

下面是我寫的兩個實例,你只須要在窗體 FormMain 中添加一個 button 和一個 textbox 便可,默認名爲 Button1TextBox1spa

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 便可當即執行裏面的代碼。
若是擁有返回值,程序還能夠獲取代碼的返回值,但有可能須要你進行拆箱處理。code

C#代碼orm

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#的編譯器沒有導入全局命名空間的參數,所以須要在模板類裏寫入你須要導入的命名空間。
其餘的用法基本都同樣。blog

PS: 我有空再寫一點與第三方腳本庫進行交互的代碼示例。ip

相關文章
相關標籤/搜索