Here is a simple tutorial on how to read, compile, and run code that is written in a text file. In this example, we will be reading properly formatted VB.NET code from a text file which will change the text of a TextBox in a Windows Forms application to "Hello vbCity!!" when a button is pressed.
frmMain
Here is the code contained in the text file (Code.txt). This file located in the project's bin directory (ensure you set it to "copy if newer") in the file properties section of Visual Studio. This is the code we will be compiling and running from our WinForms application.
txtOutput.Text = "Hello vbCity!!"
Now, to the meat of the sample. There are a few simple steps we must complete to read, compile, and run the code. In order:
- Read the code from the file.
- Create an instance of a VB.NET compiler
- Create compiler parameters and pass them to the compiler
- Compile the code
- Check for errors
- Create an instance of the class containing the code
- Create arguments to pass to our compiled code
- Execute the code and see results
All these steps are in order in the following code fragment. This code is executed when the "Press Me" button is clicked.
Imports System.IO
Imports System.Reflection
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Public Class frmMain
' Button to start compiling code.
Private Sub btnPressMe_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPressMe.Click
' Read code from file
Dim input = My.Computer.FileSystem.ReadAllText("Code.txt")
' Create "code" literal to pass to the compiler.
'
' Notice the <% = input % > where the code read from the text file (Code.txt)
' is inserted into the code fragment.
Dim code = <code>
Imports System
Imports System.Windows.Forms
Public Class TempClass
Public Sub UpdateText(ByVal txtOutput As TextBox)
<%= input %>
End Sub
End Class
</code>
' Create the VB.NET compiler.
Dim vbProv = New VBCodeProvider()
' Create parameters to pass to the compiler.
Dim vbParams = New CompilerParameters()
' Add referenced assemblies.
vbParams.ReferencedAssemblies.Add("mscorlib.dll")
vbParams.ReferencedAssemblies.Add("System.dll")
vbParams.ReferencedAssemblies.Add("System.Windows.Forms.dll")
vbParams.GenerateExecutable = False
' Ensure we generate an assembly in memory and not as a physical file.
vbParams.GenerateInMemory = True
' Compile the code and get the compiler results (contains errors, etc.)
Dim compResults = vbProv.CompileAssemblyFromSource(vbParams, code.Value)
' Check for compile errors
If compResults.Errors.Count > 0 Then
' Show each error.
For Each er In compResults.Errors
MessageBox.Show(er.ToString())
Next
Else
' Create instance of the temporary compiled class.
Dim obj As Object = compResults.CompiledAssembly.CreateInstance("TempClass")
' An array of object that represent the arguments to be passed to our method (UpdateText).
Dim args() As Object = {Me.txtOutput}
' Execute the method by passing the method name and arguments.
Dim t As Type = obj.GetType().InvokeMember("UpdateText", BindingFlags.InvokeMethod, Nothing, obj, args)
End If
End Sub
End Class
When ran, the output show display:
You could also change the text to something a little more elaborate, such as:
Dim sum As Integer = 0
For i As Integer = 1 To 100 Step 10
sum += i
Next
txtOutput.Text = Convert.ToString(sum)
When executed, it will output:
And for a C# version (minor changes):
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 System.IO;
using System.Reflection;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.CodeDom;
namespace WinFormCodeCompile
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Load code from file
StreamReader sReader = new StreamReader(@"Code.txt");
string input = sReader.ReadToEnd();
sReader.Close();
// Code literal
string code =
@"using System;
using System.Windows.Forms;
namespace WinFormCodeCompile
{
public class Transform
{
public void UpdateText(TextBox textBox1)
{" + input + @"
}
}
}";
// Compile code
CSharpCodeProvider cProv = new CSharpCodeProvider();
CompilerParameters cParams = new CompilerParameters();
cParams.ReferencedAssemblies.Add("mscorlib.dll");
cParams.ReferencedAssemblies.Add("System.dll");
cParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
cParams.GenerateExecutable = false;
cParams.GenerateInMemory = true;
CompilerResults cResults = cProv.CompileAssemblyFromSource(cParams, code);
// Check for errors
if (cResults.Errors.Count != 0)
{
foreach (var er in cResults.Errors)
{
MessageBox.Show(er.ToString());
}
}
else
{
// Attempt to execute method.
object obj = cResults.CompiledAssembly.CreateInstance("WinFormCodeCompile.Transform");
Type t = obj.GetType();
object[] arg = { this.textBox1 }; // Pass our textbox to the method
t.InvokeMember("UpdateText", BindingFlags.InvokeMethod, null, obj, arg);
}
}
}
}
Posted
Apr 24 2010, 07:46 AM
by
James Atkinson