您的位置:首页 > 编程语言 > VB

动态执行 VB.NET 和 C# 代码

2017-08-12 22:08 567 查看
有时候我们需要尝试动态地与一些代码进行交互,而不是只能执行程序内已编死的代码,那该怎么办呢?

我首先推荐各种脚本语言,如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: 我有空再写一点与第三方脚本库进行交互的代码示例。

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: