Introduction
This program compiles a piece of code in memory and runs it dynamically, so you can test some C# or VB.NET code without having to create a new project or solution.
Background
Very often, we need test a piece of code ( C# or VB.NET ). In .NET environment, we use "csc
" or "vbc
" command; In VS.NET, we create a new project or solution, and create many other files. Both of them are tedious, especially when we test a simple program. One way is produced by jconwell, "Dot Net Script". He uses XML files ( named "dnml" ) which include C# or VB code. In that method, a dnml file is parsed and the code is compiled in memory. That's a good idea! However, it is a little inconvenient because it needs to build a new file. Here, we provide a tool based on his work. The tool has these characters:
- It can load C# or VB code file, compile the code in memory, and run the code through a
static
method ( "entry point" ); - As for VB.NET code, if it is a "
Form
", the tool can add "Main()
" to run the form; - It uses a new thread to run the code;
- It has a visual interface(see below).
Using the Tool
As the picture below shows, press button "Open..." can Open C# or VB.NET file; press button "Paste" to paste code from clipboard to text area; check box "C#" means if the code is C# language; the text box is filled with entry point (a static
method in C# or a shared Sub
or Function
in VB.NET); press button "Run!
" to compile and run the code.
How It Works
The main class of the program is CompileEngine
, which has such a constructor:
public CompileEngine( string code, LanguageType language, string entry )
{
this.SourceCode = code;
this.Language = language;
this.EntryPoint = entry;
}
Run
is an important method. It includes three steps: PrepareRealSourceCode
, CreateAssembly
and CallEntry
.
public void Run()
{
ClearErrMsgs();
string strRealSourceCode = PrepareRealSourceCode();
Assembly assembly = CreateAssembly( strRealSourceCode );
CallEntry( assembly, EntryPoint );
DisplayErrorMsg();
}
PrepareRealSourceCode
has two functions: to add "Imports
" statements and to add "Sub Main
" for VB.NET Windows Forms.
private string PrepareRealSourceCode()
{
string strRealSourceCode = "";
string strUsingStatement = "";
if( Language == LanguageType.VB )
{
string [] basicImportsStatement = {
"Imports Microsoft.VisualBasic",
"Imports System",
"Imports System.Windows.Forms",
"Imports System.Drawing",
"Imports System.Data",
"Imports System.Threading",
"Imports System.Xml",
"Imports System.Collections",
"Imports System.Diagnostics",
};
foreach( string si in basicImportsStatement )
{
if( SourceCode.IndexOf( si ) < 0 )
strUsingStatement += si + "\r\n";
}
}
strRealSourceCode = strUsingStatement + SourceCode;
if( Language == LanguageType.VB && EntryPoint == "Main" &&
strRealSourceCode.IndexOf( "Sub Main(") < 0 )
{
try
{
int posClass = strRealSourceCode.IndexOf( "Public Class ")+
"Public Class ".Length;
int posClassEnd = strRealSourceCode.IndexOf( "\r\n", posClass );
string className = strRealSourceCode.Substring( posClass,
posClassEnd - posClass );
int pos = strRealSourceCode.LastIndexOf( "End Class");
if( pos > 0 )
strRealSourceCode = strRealSourceCode.Substring( 0, pos ) + @"
Private Shared Sub Main()
" + "Dim frm As New " + className + "()" + @"
If TypeOf frm Is Form Then frm.ShowDialog()
End Sub
" + strRealSourceCode.Substring( pos );
}
catch{}
}
return strRealSourceCode;
}
CreateAssembly
compiles the source code and makes assembly in memory.
private Assembly CreateAssembly(string strRealSourceCode)
{
CodeDomProvider codeProvider = null;
if (Language == LanguageType.CSharp )
codeProvider = new CSharpCodeProvider();
else if( Language == LanguageType.VB )
codeProvider = new VBCodeProvider();
ICodeCompiler compiler = codeProvider.CreateCompiler();
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.CompilerOptions = "/target:library";
compilerParams.GenerateExecutable = false;
compilerParams.GenerateInMemory = true;
compilerParams.IncludeDebugInformation = false;
compilerParams.ReferencedAssemblies.Add( "mscorlib.dll");
compilerParams.ReferencedAssemblies.Add( "System.dll");
compilerParams.ReferencedAssemblies.Add( "System.Data.dll" );
compilerParams.ReferencedAssemblies.Add( "System.Drawing.dll" );
compilerParams.ReferencedAssemblies.Add( "System.Xml.dll" );
compilerParams.ReferencedAssemblies.Add(
"System.Windows.Forms.dll" );
if( Language == LanguageType.VB )
{
compilerParams.ReferencedAssemblies.Add(
"Microsoft.VisualBasic.dll" );
}
CompilerResults results = compiler.CompileAssemblyFromSource(
compilerParams,
strRealSourceCode );
Assembly generatedAssembly = results.CompiledAssembly;
return generatedAssembly;
}
CallEntry(Assembly...)
is used for call entrypoint by reflection.
private void CallEntry(Assembly assembly, string entryPoint)
{
try
{
Module[] mods = assembly.GetModules(false);
Type[] types = mods[0].GetTypes();
foreach (Type type in types)
{
MethodInfo mi = type.GetMethod(entryPoint,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
if (mi != null)
{
if( mi.GetParameters().Length == 1 )
{
if( mi.GetParameters()[0].ParameterType.IsArray )
{
string [] par = new string[1];
mi.Invoke(null, par);
}
}
else
{
mi.Invoke(null, null);
}
return;
}
}
}
catch (Exception ex)
{
}
}
In the FormMain
, make a new thread to make a CompileEngine
instance and call Run
.
private void btnRun_Click(object sender, System.EventArgs e)
{
new Thread( new ThreadStart( RunTheProg ) ).Start();
}
private void RunTheProg()
{
string code = txtSource.Text.Trim();
LanguageType language = chkIsCSharp.Checked ?
LanguageType.CSharp : LanguageType.VB;
string entry = txtEntry.Text.Trim();
if( code == "" ) return;
if( entry == "" ) entry = "Main";
CompileEngine engine = new CompileEngine( code, language, entry );
engine.Run();
}
Some Things That Can Improve
In the PrepareRealSourceCode
, it better to use regular expressions to find class name.
Acknowledgement
Thanks jconwell, VictorV, George Orwell, Eric Astor and other friends, who have shown me many excellent works on this topic, such as Dot Net Script, SnippetCompiler, Runtime Compilation.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.