Generating .net assemblies from within .net isn't so hard. Here is an example, I need to flesh it out a fair bit but its pretty easy to follow.
I have used this approach for generating code in some of my apps.
Basic Code Compilation Helper Function
/// <summary>
/// Creates an executable file from the supplied <paramref name="code"/>.
/// </summary>
/// <param name="code">The code.</param>
/// <param name="referencedAssemblies">The referenced assemblies (apart from "system.dll").</param>
/// <param name="exeName">Name of the exe, e.g. "HelloWorld".</param>
/// <returns>The path of the new exe.</returns>
public static string CreateExe(string code, string mainClass, string[] referencedAssemblies, string exeName)
{
// Create an instance of the CSharpCodeProvider to compile the C# code
CodeDomProvider codeProvider = new CSharpCodeProvider();
//create the language specific code compiler
//ICodeCompiler compiler = codeProvider.CreateCompiler();
//add compiler parameters
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.GenerateExecutable = true; // .exe
compilerParams.OutputAssembly = exeName + ".exe";
compilerParams.CompilerOptions = "/optimize";
compilerParams.GenerateInMemory = false; // will get written to disk
compilerParams.IncludeDebugInformation = false; // i.e. release code
compilerParams.TempFiles = new TempFileCollection(".", true);
compilerParams.MainClass = mainClass;
compilerParams.ReferencedAssemblies.Add("system.dll");
if (referencedAssemblies != null)
{
foreach (string reference in referencedAssemblies)
{
compilerParams.ReferencedAssemblies.Add(reference);
}
}
//actually compile the code
CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, code);
//Do we have any compiler errors
if (results.Errors.Count > 0)
{
string errors = string.Empty;
foreach (CompilerError error in results.Errors)
errors += error.ToString() + Environment.NewLine;
throw new Exception(errors);
}
return results.PathToAssembly;
}
Calling the Helper Function
Call the function in a method similar to:
static void Main(string[] args)
{
// I appoligize in advance for this program:
string sourceCode = @"
using System;
namespace HelloWorld
{
public class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Sorry, but hello world..."");
}
}
}";
string path = AssemblyUtility.CreateExe(
sourceCode, "HelloWorld.Program", null, "HelloWorld");
Console.WriteLine("New exe at: " + path);
}
Basically this will create an executable file called "HelloWorld.exe" (I hate that example!
).
Examining the Results
In the code above I chose to leave the temporary files in the build directory [compilerParams.TempFiles = new TempFileCollection(".", true)]. If you run the sample application you will fild the following files created:
- 2ng5ojw-.0.cs - contains the source code to compile
- 2ng5ojw-.cmdline - the commandline options passed to the C# compiler
- 2ng5ojw-.err - nothing in this case but error text if we encountered any
- 2ng5ojw-.out - console output from the C# compiler
- 2ng5ojw-.tmp - an (empty) temp file
- HelloWorld.exe - the resulting executable
Example Source Code: PKSoftware.Net.SimpleAssemblyGeneration.zip (3.77 KB)
So Whats So Good About That?
Not so hard? Why do I need the CSharpCodeProvider etc? The real power of code compilation comes when you start building DLL's in memory [compilerParams.GenerateInMemory = true]. You can then get the resulting compiled Assembly at runtime from the compiler result [results.CompiledAssembly].
Using an Assembly thats been Generated on the Fly
If you generate a new assembly on the fly you can use it one of 2 ways (or you can save it to disk and link to it etc.)
Method 1 - Use reflection. Upside, extreemly flexible. Downside, slow.
Method 2 - Have your generated code implement an interface or inherit from a base clase that the running code already knows about. This is my prefered method. Its far more effecient. How?
BaseClass CreateClassInstance(Assembly assemblyReference, string className)
{
return assemblyReference.CreateInstance(className) as BaseClass;
}
When I get around to it I will post some updated code for these extentions. Go crazy 