|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThis is a bit of a strange article, and may not be that useful, but I think
its a very interesting subject, that some will probably not even be aware of.
This article will cover some of the less known namespaces within .NET. Such
as Why Would I Want To Create New Source Code And Applications At RuntimeWell the reason that I started looking into this was dynamic code generation for Data Access Layers (DAL) /Business Access Layer (BAL) code based on an initial schema. So thats one possible area where it may be useful to create code at runtime, basically have an application create a bunch of auto generated source code files that can then be used within another project. Thats just where we (at work) found a use for this stuff. This led me onto investigating the the The rest of this article will be split roughly between a discussion about the
System.CodeDomThe I shall demonstrate a few examples of using the CodeDom.
Obviously this only gives you a very small example of what CodeDom is like to work with. I have to say the syntax is very verbose, but the fact that you can create any .NET source from CodeDom, makes it quite appealing. There are also some good wrappers around CodeDom, which shorten the syntax, I have included a list of related articles at the bottom of this article, should you wish to read around this subject a bit more. System.CodeDom.CompilerIf we want to be able to use the For example this excerpt of the demo applications code, shows how we built
a new source code file using the /// <summary>
/// Generates the source code by calling the <see cref="CodeDomExample">GenerateCode</see> method
/// </summary>
private void picGenerate_Click(object sender, EventArgs e)
{
CodeDomProvider domProvider = (CodeDomProvider)getCodeEditorSyntaxOrProvdiderForLanguage
(cmbPickLanguage.SelectedItem.ToString(),false);
//create the code
cde.GenerateCode(domProvider, cde.BuildSimpleAdder());
// Display the generated source code file
using (StreamReader sr = new StreamReader(getSourceFileName(domProvider)))
{
txtCode.Document.Text = sr.ReadToEnd();
sr.Close();
}
}
....
....
/// <summary>
/// Returns either a SyntaxLanguage or a CodeDomProvider based on the selectedLanguage
/// </summary>
/// <param name="selectedLanguage">A string respresenting the selected language</param>
/// <param name="syntaxObjectRequired">True if this method should return a SyntaxLanguage object</param>
/// <returns></returns>
private object getCodeEditorSyntaxOrProvdiderForLanguage(string selectedLanguage, bool syntaxObjectRequired)
{
switch (selectedLanguage)
{
case "CSharp":
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
case "Visual Basic":
if (syntaxObjectRequired)
return SyntaxLanguage.VBNET;
else
return CodeDomProvider.CreateProvider("VisualBasic");
default:
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
}
}
....
....
/// <summary>
/// Returns a string respresenting the filename for the generated app, which is based
/// on the extension of the current <see cref="CodeDomProvider">CodeDomProvider</see>
/// </summary>
/// <param name="domProvider">The current <see cref="CodeDomProvider">CodeDomProvider</see></param>
/// <returns>A string respresenting the filename for the generated app</returns>
private string getSourceFileName(CodeDomProvider domProvider)
{
if (domProvider.FileExtension[0] == '.')
{
return "app" + domProvider.FileExtension;
}
else
{
return "app." + domProvider.FileExtension;
}
}
....
....
/// <summary>
/// Uses the CodeDom classes to create a simply
/// built in run time source code file (graph). And returns
/// a <see cref="CodeCompileUnit">CodeCompileUnit</see>
/// that can be used by the <see cref="CompilerParameters">
/// CompilerParameters</see> to build an actual
/// <see cref="Assembly">Assembly</see>
/// </summary>
/// <returns></returns>
public CodeCompileUnit BuildSimpleAdder()
{
CodeCompileUnit compileUnit = new CodeCompileUnit();
compileUnit.Namespaces.Add(InitializeNameSpace("TestApp"));
CodeTypeDeclaration ctd = CreateClass("SimplePrint");
// Add the class to the namespace
compileUnit.Namespaces[0].Types.Add(ctd);
CodeEntryPointMethod mtd = CreateMainMethod();
ctd.Members.Add(mtd);
CodeVariableDeclarationStatement VariableDeclaration =
DeclareVariables(typeof(StringBuilder), "sbMessage");
// Add the variable declaration to the method
mtd.Statements.Add(VariableDeclaration);
//and print the results
mtd.Statements.Add(new CodeSnippetExpression(
"Console.WriteLine(sbMessage.Append(\"the result of adding (1+2) is \" + " +
"(1+2).ToString()))"));
// Build a call to System.Console.ReadLine, to keep console window alive to see results
CodeTypeReferenceExpression csSystemConsoleType =
new CodeTypeReferenceExpression("System.Console");
CodeMethodInvokeExpression csReadLine =
new CodeMethodInvokeExpression(csSystemConsoleType, "ReadLine");
//// Add the ReadLine statement.
mtd.Statements.Add(csReadLine);
//lastly return it all
return compileUnit;
}
....
....
/// <summary>
/// Generates the code using either a CSharp or Visual Basic
/// CodeDomProvider. This generated code uses the
/// <see cref="CodeCompileUnit">CodeCompileUnit</see> that was created
/// by the BuildSimpleAdder method
/// </summary>
/// <param name="domProvider">The CodeDomProvider to use, CSharp or Visual Basic CodeDomProviders in our case</param>
/// <param name="compileunit">A <see cref="CodeCompileUnit">CodeCompileUnit</see> that can be used by
/// the System.CodeDom.Compiler classes</param>
public void GenerateCode(CodeDomProvider domProvider, CodeCompileUnit compileunit)
{
//builds the source based on the selected language
String srcFile;
if (domProvider.FileExtension[0] == '.')
{
srcFile = "app" + domProvider.FileExtension;
}
else
{
srcFile = "app." + domProvider.FileExtension;
}
//we want a nice indemtable output file
using (IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(srcFile, false), " "))
{
// Create the source code
domProvider.GenerateCodeFromCompileUnit(compileunit, tw, new CodeGeneratorOptions());
tw.Close();
}
}
So doing this is enough to create a new source code file, in any of the .NET
languages. This is all thanks for the So what have we got so far, well we create a DOM representing a new class,
which eventually gave us a So thats cool, we have a source code file. But at the top of this article I also said that I would show you how to compile source code into an Assembly or an Exe. So lets continue to look at that. Again I think the best thing to show you is the relavant source code. /// <summary>
/// Compile the source code by calling the <see cref="CodeDomExample">Compile</see> method
/// </summary>
private void picCompile_Click(object sender, EventArgs e)
{
CodeDomProvider domProvider = (CodeDomProvider)getCodeEditorSyntaxOrProvdiderForLanguage
(cmbPickLanguage.SelectedItem.ToString(), false);
// Compile the source file into an executable output file.
string srcFile = getSourceFileName(domProvider);
CompilerResults cr = cde.Compile(domProvider,srcFile,"app.exe");
string message = string.Empty;
if (cr.Errors.Count > 0)
{
// Display compilation errors.
message+= "Errors encountered while building " +
srcFile + " into " + cr.PathToAssembly + ": \r\n\n";
foreach (CompilerError ce in cr.Errors)
message +=ce.ToString() + "\r\n";
picRun.Enabled = false;
MessageBox.Show(message);
}
else
{
message += "Source " + srcFile + " built into " +
cr.PathToAssembly + " with no errors.";
picRun.Enabled = true;
MessageBox.Show(message);
}
}
....
....
/// <summary>
/// Returns either a SyntaxLanguage or a CodeDomProvider based on the selectedLanguage
/// </summary>
/// <param name="selectedLanguage">A string respresenting the selected language</param>
/// <param name="syntaxObjectRequired">True if this method should return a SyntaxLanguage object</param>
/// <returns></returns>
private object getCodeEditorSyntaxOrProvdiderForLanguage(string selectedLanguage, bool syntaxObjectRequired)
{
switch (selectedLanguage)
{
case "CSharp":
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
case "Visual Basic":
if (syntaxObjectRequired)
return SyntaxLanguage.VBNET;
else
return CodeDomProvider.CreateProvider("VisualBasic");
default:
if (syntaxObjectRequired)
return SyntaxLanguage.CSharp;
else
return CodeDomProvider.CreateProvider("CSharp");
}
}
....
....
/// <summary>
/// Returns a string respresenting the filename for the generated app, which is based
/// on the extension of the current <see cref="CodeDomProvider">CodeDomProvider</see>
/// </summary>
/// <param name="domProvider">The current <see cref="CodeDomProvider">CodeDomProvider</see></param>
/// <returns>A string respresenting the filename for the generated app</returns>
private string getSourceFileName(CodeDomProvider domProvider)
{
if (domProvider.FileExtension[0] == '.')
{
return "app" + domProvider.FileExtension;
}
else
{
return "app." + domProvider.FileExtension;
}
}
....
....
/// <summary>
/// Uses the <see cref="CompilerParameters">CompilerParameters</see>
/// to compile the the source file, creating the output file
/// </summary>
/// <param name="domProvider">The CodeDomProvider to use, CSharp or Visual Basic CodeDomProviders in our case</param>
/// <param name="srcFile">The source file</param>
/// <param name="exeFile">The name for the output file</param>
/// <returns>The actual <see cref="CompilerResults">CompilerResults</see>
/// which will holds the compiled <see cref="Assembly">Assembly</see></returns>
public CompilerResults Compile(CodeDomProvider domProvider, String srcFile,String exeFile)
{
// Configure a CompilerParameters that links System.dll
// and produces the specified executable file.
String[] referenceAssemblies = { "System.dll" };
CompilerParameters cp = new CompilerParameters(referenceAssemblies,exeFile, false);
//make sure we create an EXE not a Dll, and then compile
cp.GenerateExecutable = true;
CompilerResults compiledResults = domProvider.CompileAssemblyFromFile(cp, srcFile);
return compiledResults;
}
And that is enough to get us a EXE created for the source code that we created earlier. Remember this is all being done at runtime, I think thats
pretty cool actually. Create a new source code and compile and run it at runtime. That's neat.
The DEMO ApplicationAnyway, lets just have a quick chat about the demo app. I use the fantastic Fireball code highlighter, available right here at Codeproject. And the basic program that I am trying to create at runtime is as follows Namespace TestApp {
using System;
using System.Text;
Public Class SimplePrint {
Public Static void Main() {
System.Text.StringBuilder sbMessage = new System.Text.StringBuilder();
Console.WriteLine(sbMessage.Append("the result of adding (1+2) is " + (1+2).ToString()));
System.Console.ReadLine();
}
}
}
Which when generated in Visual Basic .NET, looks as follows: Option Strict Off
Option Explicit On
Imports System
Imports System.Text
Namespace TestApp
Public Class SimplePrint
Public Shared Sub Main()
Dim sbMessage As System.Text.StringBuilder = New System.Text.StringBuilder
Console.WriteLine(sbMessage.Append("the result of adding (1+2) is " + (1+2).ToString()))
System.Console.ReadLine
End Sub
End Class
End Namespace
And the demo application looks like this, where there are 3 buttons:
Here is a screen shot of the demo app, just after a generate and compile being performed, where the selected .NET language was Visual Basic
And here is the actual newly created application runnning
And thats about it. Like I say, you are by no means an expert from reading this, but I hope its given you an idea about what can be done with these 2 very cool namespaces. Other CodeDom Resourcesmsdn2.microsoft.com/en-us/library/system.codedom.aspx msdn2.microsoft.com/en-us/library/system.codedom.compiler(VS.71).aspx http://msdn2.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.aspx http://www.codeproject.com/KB/recipes/codedomparser.aspx http://www.codeproject.com/KB/architecture/codedompatterns.aspx#CodePatternCompileUnit http://www.codeproject.com/KB/database/ObjectMapping2.aspx Future WorkI may end up elaborating on this article in the fiture to discuss how the process of code generation can be handled more efficienly using some of the Visual Studio SDK tools. History27/01/08 : Initial Issue
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||