The CodeDom feature of the .NET Framework is extremely powerful in that it provides the infrastructure for dynamically creating assemblies within your own applications. Code generation of this type is very flexible and opens up some interesting scenarios, such as generating the data access layer (DAL) at first-run of your application.
All is not rosy with CodeDom as there are some notable limitations. For example, there is no way to generate P/Invoke declarations. Another limitation is the apparently no way of instructing the CodeProvider to generate a .NET Compact Framework assembly. However with a few tricks borrowed from MSBuild it can be done and I will show you how.
The process of dynamically generating an assembly can be distilled down to 3-step procedure. The first step is to construct the DOM for the code that will be compiled later. This typically involves creating a type within a namespace and creating the members of the type. For simplicity, I created a type with a single static method in the example code attached to this article.
The second step in the procedure is to initialize the CodeProvider which will be use to either generate source code or an assembly from the DOM. This is also the step where you specify the parameters that will be passed to either csc.exe or vbc.exe, depending on the CodeProvider you use. It is these compiler parameters that hold the key to generating a .NET Compact Framework compatible assembly. In order to know what parameters the compiler needs, you can take a look at what MSBuild does when you compile your solution in Visual Studio.
If you look at the Output window when you build a C# solution, you may see something like the output below:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Csc.exe /noconfig /nowarn:1701,1702 /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE;WindowsCE /reference:"C:\Program Files\Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE\mscorlib.dll" /reference:"C:\Program Files\Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE\System.dll" /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\CGTest.exe /target:exe Program.cs Properties\AssemblyInfo.cs
It should be obvious that MSBuild is calling the C# compiler (csc.exe). You will notice I've highlighted some of the parameters being passed to csc.exe. These are the 3 parameters that distinguish a .NET Framework build from a .NET Compact Framework build. What each of these parameters does is described below.
- /noconfig: This instructs the compiler to not process the response file, csc.rsp. this will have the effect removing all the default compiler options.
- /nostdlib: This tells the compiler not to reference the standard library (mscorlib.dll). This gives us the ability to reference the .NET Compact Framework mscorlib.dll.
- /reference: This is an explicit reference to the .NET Compact Framework version of mscorlib.dll.
How can you specify these compiler options when compiling with CodeDom? Create an instance of the CompilerParameters class. The code snippet below demonstrates how to do just that.
// The CSharpCodeProvider will be used to generate the assembly
CodeDomProvider csp = new Microsoft.CSharp.CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.OutputAssembly = "CodeDomGenerated.dll";
// Specify the /target:library compiler option
cp.GenerateExecutable = false;
// Flush the assembly to disk
cp.GenerateInMemory = false;
// -------------------------------------------------- //
// You *must* specify the following compiler options //
// when compiling .NET Compact Framework assemblies //
// -------------------------------------------------- //
cp.CompilerOptions = "/noconfig /nostdlib";
// -------------------------------------------------- //
// You *must* specify the full path to the assembly //
// when adding .NET Compact Framework references //
// -------------------------------------------------- //
cp.ReferencedAssemblies.Add(
Path.Combine(GetBCLPath(), "mscorlib.dll")
);
Passing the CompilerParameters variable to the CompileFromAssemblyFromDom method of your CodeProvider instance will generate the assembly. A full example of this can downloaded here.