Learn Roslyn Now: Part 16 The Emit API

Up until now, we’ve mostly looked at how we can use Roslyn to analyze and manipulate source code. Now we’ll take a look at finishing the compilation process by emitting it disk or to memory. To start, we’ll just try emitting a simple compilation to disk and checking whether or not it succeeded.


var tree = CSharpSyntaxTree.ParseText(@"
using System;
public class C
{
public static void Main()
{
Console.WriteLine(""Hello World!"");
Console.ReadLine();
}
}");
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { mscorlib });
//Emitting to file is available through an extension method in the Microsoft.CodeAnalysis namespace
var emitResult = compilation.Emit("output.exe", "output.pdb");
//If our compilation failed, we can discover exactly why.
if(!emitResult.Success)
{
foreach(var diagnostic in emitResult.Diagnostics)
{
Console.WriteLine(diagnostic.ToString());
}
}

After running this code we can see that our executable and .pdb have been emitted to Debug/bin/. We can double click output.exe and see that our program runs as expected. Keep in mind that the .pdb file is optional. I’ve only chosen to emit it here to show off the API. Writing the .pdb file to disk can take a fairly long time and it often pays to omit this argument unless you really need it.

Sometimes we might not want to emit to disk. We might just want to compile the code, emit it to memory and then execute it from memory. Keep in mind that for most cases where we’d want to do this, the scripting API probably makes more sense to use. Still, it pays to know our options.


var tree = CSharpSyntaxTree.ParseText(@"
using System;
public class MyClass
{
public static void Main()
{
Console.WriteLine(""Hello World!"");
Console.ReadLine();
}
}");
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { mscorlib });
//Emit to stream
var ms = new MemoryStream();
var emitResult = compilation.Emit(ms);
//Load into currently running assembly. Normally we'd probably
//want to do this in an AppDomain
var ourAssembly = Assembly.Load(ms.ToArray());
var type = ourAssembly.GetType("MyClass");
//Invokes our main method and writes "Hello World" 🙂
type.InvokeMember("Main", BindingFlags.Default | BindingFlags.InvokeMethod, null, null, null);

Finally, what if we want to influence  how our code is compiled? We might want to allow unsafe code, mark warnings as errors or delay sign the assembly. All of these options can be customized by passing a CSharpCompilationOptions object to CSharpCompilation.Create(). We’ll take a look at how we can interact with a few of these properties below.


var tree = CSharpSyntaxTree.ParseText(@"
using System;
public class MyClass
{
public static void Main()
{
Console.WriteLine(""Hello World!"");
Console.ReadLine();
}
}");
//We first have to choose what kind of output we're creating: DLL, .exe etc.
var options = new CSharpCompilationOptions(OutputKind.ConsoleApplication);
options = options.WithAllowUnsafe(true); //Allow unsafe code;
options = options.WithOptimizationLevel(OptimizationLevel.Release); //Set optimization level
options = options.WithPlatform(Platform.X64); //Set platform
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree },
references: new[] { mscorlib },
options: options); //Pass options to compilation

In total there are about twenty-five different options available for customization. Basically any option you have within the Visual Studio’s project property page should be available here.

Advanced options

There are a few optional parameters available in Compilation.Emit() that are worth discussing. Some of them I’m familiar with, but others I’ve never used.

  • xmlDocPath – Auto generates XML documentation based on the documentation comments present on your classes, methods, properties etc.
  • manifestResources – Allows you to manually embed resources such as strings and images within the emitted assembly. Batteries are not included with this API and it requires some heavy lifting if you want to embed .resx resources within your assembly. We’ll explore this overload in a future blog post.
  • win32ResourcesPath – Path of the file from which the compilation’s Win32 resources will be read (in RES format). Unfortunately I haven’t used this API yet and I’m not at all familiar with Win32 Resources.
  • There is also the option to EmitDifference between two compilations. I’m not familiar with this API, and I’m not familiar with how you can apply these deltas to existing assemblies on disk or in memory. I hope to learn more about this API in the coming months.

That just about wraps up the Emit API. If you have any questions, feel free to ask them in the comments below.

19 thoughts on “Learn Roslyn Now: Part 16 The Emit API

  1. Nice post as always.
    The EmitDifference is how Edit And Continue works. In general, you need to create a baseline compilation and then you can get the deltas between the baesline and the current state.

    1. Thanks 🙂

      RE: EmitDifference: I can create the baseline and I think the EmitDifference, but I’m unsure of how I can apply this to an existing compilation either on disk or in memory. Do you have experience with this?

      1. Thanks for the examples. I’ve looked at them closely, but I don’t think they allow users to apply changes to existing DLLs. They emit the differences as two delta streams, one for IL and one for PDBs which are then passed to the CLR. The CLR does the heavy lifting here and applies the deltas to the existing process.

        The interface VS’s debugger uses: https://msdn.microsoft.com/en-us/library/ms231880%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

        The implementation in CoreCLR: https://github.com/dotnet/coreclr/blob/bc146608854d1db9cdbcc0b08029a87754e12b49/src/debug/di/module.cpp#L2157

  2. Great articles! I was curious if there was a way to Emit to memory streams like:
    using( var dllStream = new System.IO.MemoryStream() )
    using( var pdbStream = new System.IO.MemoryStream() )
    {
    var result = compilation.Emit( dllStream, pdbStream );

    and then load the new assembly and pdb:

    var assembly = System.Reflection.Assembly.Load( dllStream.ToArray(), pdbStream.ToArray() );

    When I do this the assembly loads, but Visual Studio’s output logs:
    Loaded ‘Test.dll’. Cannot find or open the PDB file.

    Is there something I’m doing wrong, or is it just not possible to do this?

      1. Oddly it is still logging the same error, but it seems to be working and I’m able to set breakpoints in the test file. I was passing in the wrong Text.Encoding to ParseText() at first (I was passing in UTF8 instead of ASCII) and that was causing the debugger to think the file had changed. So it seems to be working fine now except for the VS log that claims it cannot find or open the PDB file.

      2. One last question. Do you know if there is a way to enable Edit & Continue when loading using Assembly.Load?

      3. Sorry I just noticed your last comment today. EnC as I understand requires two processes. One (the debugger process) must pause the other’s CLR so code stops being executed and instead tell the CLR to load new code before telling it to continue. Is this how you’re trying to do things with Assembly.Load?

        I’m not aware of any restrictions on assemblies loaded with Assembly.Load. In fact, that’s how I’m using EnC. Are you seeing errors?

  3. I’m doing a CSharpCompilation.Emit() into a memory stream and then a Assembly.Load() on that. The debugger will hit breakpoints in the file loaded by the assembly load, but EnC does not occur if I make a change and then step. The (older) docs seem to mention it not working when using ‘Attach To’ (vs. starting the application in VS), so I’m guessing it’s something along those lines. I’ll have to try EnC on an assembly built normally, but loaded with Assembly.Load.

  4. error CS0103: The name ‘Console’ does not exist in the current context

    on console.writeline() and console.readline()

    why do you think this is happening?

  5. At first – Great articles!!

    Is it possible to compile code dynamically from a signed assembly and run this code from the signed assembly? Has the compiled code access to the code of another signed assembly in my infrastructure?
    Can I call the signed Assembly code from my dynamically generated code?

    Questions about questions 😉

    Thanks a lot for your time and your help.

  6. I don’t know whether it’s bug or not.
    When we generate class using Roslyn and build it into a dll, then we consume that dll in another project, IF we use a decimal (System.Decimal) type inside our generated class, then when we compile that another project, it always give this error:

    The type ‘Decimal’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e’.

    any clue?

    1. Are you targeting UWP? I have never targeted it myself or seen that error. Here are two links regarding that DLL:

      https://stackoverflow.com/questions/50010855/could-not-load-file-or-assembly-system-private-corelib

      https://blogs.msdn.microsoft.com/appconsult/2018/05/15/could-not-load-file-or-assembly-system-private-corelib/

      If you can provide a minimum repro they may be able to help you directly at the Roslyn GitHub: https://github.com/dotnet/roslyn

  7. I’m using roslyn (Microsoft.CodeAnalysis.CSharp, Microsoft.CodeAnalysis.CSharp.Workspaces) in a .NET Framework v4.6.2 console application. I dynamically create the syntax tree and build it (Emit) into an assembly. This assembly target framework and version was the same as the console application framework and version so it was quite straightforward to do that. That worked without issues.

    Now, in this very console application, using the same logic I want to build (Emit) an assembly targeting UWP. I’m completely stuck. Somewhere I found a few hints to use a TargetFrameworkAttribute. I tried that. I was even able to generate an assembly targeting .NET Standard by adding [assembly: TargetFramework(“netstandard2.0”)] but that is not what I need. That only proved that TargetFrameworkAttribute works at least in some cases. I particularly need to target UWP. I tried “uap”, “uap10.0”, “netcore50”, “.NETCore,Version=v5.0”, “win10” as the TargetFrameworkAttribute string. Nothing gives me the desired result.

    I found another hint that most likely I need to specify RuntimeMetadataVersion for the Emit in the EmitOptions but I have no idea what to specify there. Also, I don’t fully understand what to do with the references. For the .NET Framework I specified “mscorlib.dll”, “System.Core.dll”, “Serialize.Linq.dll”. That was all that I needed for my dynamically generated code. It seems that for UWP I should not specify “mscorlib.dll”. I analyzed other UWP assemblies, none of them have a direct reference to “mscorlib.dll”. But if I don’t specify it I get an error that I have to specify RuntimeMetadataVersion. For the UWP I tried this list of references “UAP\System.Runtime.dll”, “UAP\System.Collections.dll”, “UAP\Serialize.Linq.Universal.dll”. Basically, I used a list that I found in another UWP assembly with the similar code.

    Can anybody share an example how to use Roslyn and generate UWP assembly when the Roslyn is used in a .NET Framework console application?

Leave a reply to Doug Cancel reply