Click here to Skip to main content
Click here to Skip to main content

Compiling code during runtime

By , 8 May 2005
 

Introduction

In some cases it is very useful to compile the source code during execution. For this situation you can use the namespace Microsoft.CSharp. This namespace contains the class CSharpCodeProvider. Using the CSharpCodeProvider you can have access to the C#-compiler. Please note that you can use the compiler with VB.NET also, just use the namespace Microsoft.VisualBasic and the class VBCodeProvider. In Addition to be able to compile your code on the fly you have to add the System.CodeDom and the System.CodeDom.Compiler namespaces.

First step

In this example we will use a RichTextBox in which we'll load some example-code. After that you will be able to compile the code. In this step we'll have a closer look at the source code of the application:

CSharpCodeProvider csp = new CSharpCodeProvider();
ICodeCompiler cc = csp.CreateCompiler();

Using CSharpCodeProvider we gain access to the compiler. Using this compiler we are able to use the csc.exe without any need for the command line. To compile the example-code successfully we have to set some compiler parameters. They include which assemblies should be referred to, where the output assembly has to be stored, which warning level we want to use and some other stuff. The compiler options that we have set are the same as the compiler options you have set using the csc.exe:

CompilerParameters cp = new CompilerParameters();
cp.OutputAssembly = Application.StartupPath + "\\TestClass.dll";
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Data.dll");
cp.ReferencedAssemblies.Add("System.Xml.dll");
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");
cp.ReferencedAssemblies.Add("CSharpScripter.exe");
            
cp.WarningLevel = 3;

cp.CompilerOptions = "/target:library /optimize";
cp.GenerateExecutable = false;
cp.GenerateInMemory = false;

The CompilerParameters class has a property GenerateInMemory. We set this to false because we want to write the assembly to disk and not to memory.

The compilation

After the compilation process we want to know the results, so we create a new instance of the class CompilerResults. CompilerResults needs the parameter tempFiles. These are the files that will be generated by the compiler during compilation. Create an instance of TempFileCollection and set the path to the location where we create the temporary files. If you want to keep the files after compilation set the second parameter of the TempFileCollection to true, otherwise false:

System.CodeDom.Compiler.TempFileCollection tfc = 
                new TempFileCollection(Application.StartupPath, false);
CompilerResults cr  = new CompilerResults(tfc);

Now we can compile our example code. There are different possibilities that are given by the CodeCompiler. In our case we use the method CompileAssemblyFromSource:

cr = cc.CompileAssemblyFromSource(cp, this.rtfCode.Text);
System.Collections.Specialized.StringCollection sc = cr.Output;
foreach (string s in sc) 
{
    Console.WriteLine(s);
}

After compilation we write the created output to the console. In some cases there may be some compiling errors. You can catch them looking at the property Errors of the CompilerResults object:

if (cr.Errors.Count > 0) 
{
    foreach (CompilerError ce in cr.Errors) 
    {
        Console.WriteLine(ce.ErrorNumber + ": " + ce.ErrorText);
    }
    MessageBox.Show(this, "Errors occoured", "Errors", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
    this.btnExecute.Enabled = false;
} 
else 
{
    this.btnExecute.Enabled = true;
}

The execution

Now we've compiled our example code and want to execute it. As you can see within the source code of the sample application, I have created an interface called Command, which includes the method Execute(). Our example code is the implementation of this interface.

To execute the assembly we have to create a new application domain in which the assembly should be loaded. So that we are able to unload the assembly. We use ShadowCopyFiles, because of this we are able to override the original assembly:

AppDomainSetup ads = new AppDomainSetup();
ads.ShadowCopyFiles = "true";
AppDomain.CurrentDomain.SetShadowCopyFiles();
AppDomain newDomain = AppDomain.CreateDomain("newDomain");

We've created the new application domain, and now we want to load the assembly by doing the following lines:

byte[] rawAssembly = loadFile("TestClass.dll");
Assembly assembly = newDomain.Load(rawAssembly, null);

OK, the assembly is loaded, now we want to execute the sample code which will show us a simple MessageBox:

Command testClass = 
        (Command)assembly.CreateInstance("CSharpScripter.TestClass");
testClass.Execute();

After showing the MessageBox we will unload the assembly and the application domain:

testClass = null;
assembly = null;
AppDomain.Unload(newDomain);
newDomain = null;

History

  • 2005/09/05 - Initial article.
  • 2005/10/05 - Added how to catch errors.

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

Norbert Eder
Team Leader
Austria Austria
Member
Norbert Eder currently works as a .NET software architect at UPPER Network. He writes books and articles about WPF, Silverlight and Windows Phone 7.
 
He shares his knowledge using his weblog.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionc++memberRuth Aanie20 Feb '13 - 21:52 
How about compiling c++ code during runtime?
GeneralMy vote of 5memberversion_2.013 Jun '11 - 22:14 
Superb... Great One ... My vote of 5 ...
GeneralInvalid search path 'C:\\Program Files\\Microsoft Visual Studio .NET\\FrameworkSDK\\Lib\\memberjadoul michel14 Feb '10 - 9:07 
Hello,
 
I could not run the program.
I must be missing something.
 
The error occurs during this statement:
 
cr = cc.CompileAssemblyFromSource(cp, this.rtfCode.Text);
 
and the error message is this:
 
{warning CS1668: Invalid search path 'C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Lib\' specified in 'LIB environment variable' -- 'The system cannot find the path specified. '} object {System.CodeDom.Compiler.CompilerError}
 
I am using "C# 2008 Express Edition" and I have several older versions of C# installed.
I guess the FrameworkSDK contains what I am missing and is not part of "C# 2008 Express Edition"?
Is that right?
I guess that the missing piece is the compiler itself, altough I did not get any error during the build.
Could you clarify this?
 
Thanks.
QuestionHow about Creating assembly without C# filememberabcxyz8212 Jun '07 - 8:29 
Excellent Article, you have my 5.
 
How about generating C# code/Assembly based on external inputs. Say I want to create Interface for methods given by user through custom GUI or add some public properties to assembly?
 
Thanks
 
Regards,
MaulikCE

GeneralInteracting with the script runnermemberlshyamal31 May '07 - 20:48 
How does one make the loaded assembly work with an instance of the main application?
Generalcompiling code with assemply Info (in Properties Folder)memberabedo19827 May '07 - 9:37 
I want to add assemply info to the output of compiling Code
GeneralcrResults.Errors.Countmembernasambur20 Feb '07 - 23:14 
Hi..
 
Knowingly i made 7 to 8 mistakes in C# code and passed it to the CodeDomProvider's object..
 
after compilation it throws the errors.. but instead of displaying all the 7 errors.. it display only 4 errors.
 
i tried to set the capacity of the Errors list.. that is
crResult.Errors.Capacity to 100.
 
but still gets the same.
 
Plz help me how to get all the errors.
 
regards,
nas

GeneralRe: crResults.Errors.CountmemberNorbert Eder21 Feb '07 - 1:19 
Could it be that there are only 4 errors and the 3 left are warnings?
 
Regards,
Norbert

GeneralRe: crResults.Errors.Countmembernasambur21 Feb '07 - 16:01 
Hi Norbert,
 
no i'm sure that all are errors and i did that for testing the application..
 

regards,
nas
Questioncan a web project use "ReferencedAssemblies.Add("customer.dll")"memberwlbkeats7 Dec '06 - 23:28 
think u!
today i made a test ,but i find the same code didn't work the same.
when i do it in a winform ,it's ok.
but when i do it in a webForm,it gives message like "can't find "customer.dll"
 

the code:
public Assembly CreateAssembly(string sourceCode,string dllName)
{
if (sourceCode.Length == 0)
{
throw new System.Exception("no source code");
}

CodeDomProvider codeProvider = null;
codeProvider = new CSharpCodeProvider();
 
ICodeCompiler compiler = codeProvider.CreateCompiler();
 
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.CompilerOptions = "/target:library";
compilerParams.OutputAssembly = dllName;
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( "Neighbor.Model.dll" );
 
CompilerResults results = compiler.CompileAssemblyFromSource(
compilerParams,
sourceCode );
string errors = "" ;
if (results.Errors.Count > 0)
{
foreach (CompilerError error in results.Errors)
errors =errors + error.ErrorText ;
 
throw new Exception(errors);
}
Assembly generatedAssembly = results.CompiledAssembly;
return generatedAssembly;
}
public object GetObject(string sourceCode,string objectName )
{
//Assembly asm =this.CreateAssembly(sourceCode,"test.dll");
Assembly asm =this.CreateAssembly(sourceCode);
object o = asm.CreateInstance( objectName );
return o ;
}
 
public Assembly CreateAssembly(string strRealSourceCode)
{
 
//Create an instance whichever code provider that is needed
CodeDomProvider codeProvider = null;

codeProvider = new CSharpCodeProvider();

 
//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( "CC.dll" );
compilerParams.ReferencedAssemblies.Add( "System.Data1.dll" );
compilerParams.ReferencedAssemblies.Add( "PDT.Persist.dll" );

 

 

 
//actually compile the code
CompilerResults results = compiler.CompileAssemblyFromSource(
compilerParams,
strRealSourceCode );
string e = "" ;
//Do we have any compiler errors
if (results.Errors.Count > 0)
{
foreach (CompilerError error in results.Errors)
{
e = e + error.ErrorText;
}
 
throw new Exception(e);
}
 
//get a hold of the actual assembly that was generated
Assembly generatedAssembly = results.CompiledAssembly;
 

return generatedAssembly;
}
 


 
wlbkeats
AnswerRe: can a web project use "ReferencedAssemblies.Add("customer.dll")"memberNorbert Eder10 Dec '06 - 19:59 
You might get a SecurityException if you the immediate caller or the derived class do not have full-trust.
 
For further information have a look at http://msdn2.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx[^]
QuestionHow to debug the dynamic code ?membersakumira28 Nov '06 - 17:05 
Don't use log file, how to debug the dynamic code compiled by this way ?Frown | :(
 
I love freecode

AnswerRe: How to debug the dynamic code ?memberNorbert Eder10 Dec '06 - 19:46 
See the Errors-Collection of the CompilerResults for informations on errors. Otherwise you have to implement a real time debugger to the solution Wink | ;-)
QuestionAJmemberalmarma413 Nov '06 - 3:40 
I see your example and my question is if the code of de dll changed and recompiled the application executes this new code or executes de older code?

AnswerRe: AJmemberNorbert Eder10 Dec '06 - 19:48 
It executes the new one. You can try it in the following way: Change the ReadOnly-Property of the textfield to false. Load the test code, compile it, change the code, compile it again an execute it. You will see the new version.
GeneralRe: AJmemberzhudexiang9 Mar '07 - 2:40 
But it seems execute the old one, I use VS2005 and .NET 2.0Confused | :confused:
GeneralRe: AJmemberSanguaire3 Apr '07 - 22:03 
Hi,
 
I use this code with VS2005 and NET 2.0 to execute the new code
 

private void btnExecute_Click(object sender, System.EventArgs e)
{

byte[] rawAssembly = loadFile("TestClass.dll");
 
Assembly assembly = AppDomain.CurrentDomain.Load(rawAssembly);
 
Command testClass = (Command)assembly.CreateInstance("CSharpScripter.TestClass");
testClass.Execute(this);


 


testClass = null;
assembly = null;




}

GeneralRe: AJmemberBrianARice5 Aug '09 - 4:18 
Same here... it is *not* unloading the assembly (VS 2008 and NET 3.5).
GeneralRe: AJmembererandap6 Nov '11 - 5:23 
you can just use as follows, this will help you
 
link :
 
<a href="http://howsharepoint.blogspot.com/2011/11/run-time-xaml-code-compiler.html">run-time-xaml-code-compiler</a>

QuestionRecompile codememberDr Vespa18 Jan '06 - 5:15 
When I try to recompile the code, that has been executed, and the dll (TestClass.dll) can't be overwrited, it hasn't be liberated correctly. I've seen that the domain is unloaded but it isn't enough...
 
Any idea?
 
Thanks.
 
Marc.
AnswerRe: Recompile codememberNorbert Eder24 Jan '06 - 22:11 
Do you use .NET 1.1 or .NET 2.0?
GeneralRe: Recompile codemembercarlosgdes013 Nov '06 - 2:33 
I have the same problem winth Visual Studio 1005 and .NET 2.0
GeneralRe: Recompile codemembercarlosgdes013 Nov '06 - 2:34 
Sorry,
I have the same problem winth Visual Studio 2005 and .NET 2.0
GeneralDebug the compiled codememberOlaf Herrmann9 May '05 - 22:31 
Has anyone an idea how to debug the code of the new assembly with VS?
GeneralRe: Debug the compiled codememberNorbert Eder9 May '05 - 22:55 
Hi,
 
the CompilerResults-Object has a property Errors which is a CompilerErrorCollection containing CompileError-Objects. After compiling you can have a look at the Errors-Property to receive information if erros occoured. I've modified the article in that way.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 9 May 2005
Article Copyright 2005 by Norbert Eder
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid