Click here to Skip to main content
6,295,667 members and growing! (14,748 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » .NET Framework » Applications     Intermediate

A tool for dynamic compile and run of C# or VB.NET code

By dstang2000

A tool for dynamic compiling and running of C# or VB.NET code in memory without having to create a project
C#, Windows, .NET 1.0, Dev
Posted:22 Mar 2004
Updated:24 Mar 2004
Views:103,002
Bookmarked:45 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
16 votes for this article.
Popularity: 4.25 Rating: 3.53 out of 5
3 votes, 18.8%
1

2
2 votes, 12.5%
3
7 votes, 43.8%
4
4 votes, 25.0%
5

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 inconvenience 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 blow ).

Using the tool

As the picture blow 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 woks

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 = "";

   // add some using(Imports) statements

   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;

    // for VB Prog, Add Main(), So We Can Run It

   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 compile the source code and make assembly in memory.
  
        // compile the source, and create assembly in memory

        // this method code is mainly from jconwell, 

        // see http://www.codeproject.com/dotnet/DotNetScript.asp

        private Assembly CreateAssembly(string strRealSourceCode)
        {
            //Create an instance whichever code provider that is needed

            CodeDomProvider codeProvider = null;
            if (Language == LanguageType.CSharp )
                codeProvider = new CSharpCodeProvider();
            else if( Language == LanguageType.VB )
                codeProvider = new VBCodeProvider();

            //create the language specific code compiler

            ICodeCompiler compiler = codeProvider.CreateCompiler();

            //add compiler parameters

            CompilerParameters compilerParams = new CompilerParameters();
            compilerParams.CompilerOptions = "/target:library"; 
                   // you can add /optimize

            compilerParams.GenerateExecutable = false;
            compilerParams.GenerateInMemory = true;            
            compilerParams.IncludeDebugInformation = false;

            // add some basic references

            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" );
            }

            //actually compile the code

            CompilerResults results = compiler.CompileAssemblyFromSource(
                    compilerParams, 
                    strRealSourceCode );

            //get a hold of the actual assembly that was generated

            Assembly generatedAssembly = results.CompiledAssembly;

            //return the assembly

            return generatedAssembly;
        }

CallEntry(Assembly...) is used for call entrypoint by reflection.

  
    // invoke the entry method

    // this method code is mainly from jconwell, 

    // see http://www.codeproject.com/dotnet/DotNetScript.asp

        private void CallEntry(Assembly assembly, string entryPoint)
        {
            try
            {
                //Use reflection to call the static Main function

                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]; // if 


Main has string [] arguments
                                 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)
  {
   // we use a new thread to run it

   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, they show 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

About the Author

dstang2000


Member
He is a professional developer. He use Java, C#, VB, C and many other languages.
Occupation: Web Developer
Location: China China

Other popular .NET Framework articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 23 of 23 (Total in Forum: 23) (Refresh)FirstPrevNext
Generalcan a web project use "ReferencedAssemblies.Add("customer.dll")" Pinmemberwlbkeats0:31 8 Dec '06  
NewsDynamic Code PinmemberPrimadi0:58 6 Jun '06  
GeneralRe: Dynamic Code PinmemberLiJinwei23:48 18 Jun '08  
GeneralMemory Leak Pinmembermackenb6:07 31 Jan '06  
GeneralReferenceAssemblies.Add mine? PinmemberSkipSailors13:26 20 Jan '06  
GeneralRe: ReferenceAssemblies.Add mine? Pinmemberwlbkeats16:14 10 Dec '06  
GeneralHow to Create dll PinmemberAnu_K8:43 25 Dec '04  
GeneralRe: How to Create dll Pinmemberdstang200014:30 25 Dec '04  
GeneralRe: How to Create dll PinmemberAnu_K6:45 27 Dec '04  
GeneralRe: How to Create dll PinmemberAnu_K7:49 27 Dec '04  
GeneralGive credit where credit is due Pinmemberjconwell8:07 23 Mar '04  
GeneralRe: Give credit where credit is due PinsussAnonymous12:01 23 Mar '04  
GeneralRe: Give credit where credit is due Pinmemberjconwell12:04 23 Mar '04  
GeneralRe: Give credit where credit is due PinmemberMatthew Hazlett21:00 23 Mar '04  
GeneralRe: Give credit where credit is due Pinmemberdstang200012:39 23 Mar '04  
GeneralSnippetCompiler PinmemberVictor Vogelpoel4:22 23 Mar '04  
GeneralRe: SnippetCompiler Pinmemberdstang200013:55 24 Mar '04  
GeneralRe: SnippetCompiler PinmemberEric Engler10:44 9 Feb '05  
GeneralRe: SnippetCompiler PinmemberVictor Vogelpoel10:57 9 Feb '05  
GeneralDynamic Compilation PinmemberEric Astor2:01 23 Mar '04  
GeneralRe: Dynamic Compilation PinmemberMark Focas3:19 23 Mar '04  
GeneralRe: Dynamic Compilation Pinmemberdstang200013:52 24 Mar '04  
GeneralRe: Dynamic Compilation Pinmemberdstang200013:49 24 Mar '04  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 24 Mar 2004
Editor: Nishant Sivakumar
Copyright 2004 by dstang2000
Everything else Copyright © CodeProject, 1999-2009
Web20 | Advertise on the Code Project