Targeting the .NET 2 Framework when using AssemblyBuilder under .NET 4

When you use the Reflection.Emit classes to generate a dynamic assembly, the targeted framework for the assembly will be the version of the framework under which you are running the code that generates the dynamic assembly. For instance, you can create a dynamic assembly with the following code:

static void Main(string[] args)

{

    Console.WriteLine("Creating Test.dll under CLR version {0}", Environment.Version);

    Console.WriteLine("Creating an AssemblyName object.");

    AssemblyName satelliteAssemblyName = new AssemblyName();

    satelliteAssemblyName.Name = "Test";

 

    Console.WriteLine("Creating an AssemblyBuilder object.");

    AssemblyBuilder aBuilder =

        AppDomain.CurrentDomain.DefineDynamicAssembly(satelliteAssemblyName,

                                                      AssemblyBuilderAccess.RunAndSave);

 

    Console.WriteLine("Creating an ModuleBuilder object.");

    ModuleBuilder mBuilder = aBuilder.DefineDynamicModule("Test.dll", "Test.dll");

 

    Console.WriteLine("Persisting the dll.");

    aBuilder.Save("Test.dll");

}

 

If running under the .NET 4 framework, this code will generate a Test.dll which can only be run under the .NET 4 framework. If you open Test.dll in ILDASM.EXE and view the Manifest you can see that the metadata version is v4:

// Metadata version: v4.0.30319

Suppose you needed to run this dynamically generated assembly under the .NET 2.x framework? For instance: You are porting parts of your code, such as the code that generates dynamic assemblies, to .NET 4 -- but the code which consumes these assemblies is still running under .NET 2.

If you were to load the .NET 4 dynamic assembly in a .NET 2 application you would receive a System.BadFormatException:

Unhandled Exception: System.BadImageFormatException: This assembly is built by a

runtime newer than the currently loaded runtime and cannot be loaded.

(Exception from HRESULT: 0x8013101B)

   at System.Reflection.Assembly.nLoadFile(String path, Evidence evidence)

   at System.Reflection.Assembly.LoadFile(String path)

   at Loader.Program.Main(String[] args) in C:\ ReflectEmitConfigFile\Loader\Program.cs:line 16

 

It is possible to change the output metatadata version of the assembly generated by .NET 4 so that it can be loaded by a .NET 2.x application. To do this, use a config file for the .NET 4 application, which has a requiredRuntime attribute that references v2.x:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <startup>

    <requiredRuntime imageVersion="v2.0.50727" />

  </startup>

  </configuration>

 

With this config file applied to the .NET 4 application, it still runs under the .NET 4 runtime yet the emitted assembly will target version 2.0.50727 and will load without error in the v2 application. Note that you cannot target any .NET 4 features in the dynamic assembly generated on .NET 4 if you wish for the appllication to run under .NET 2. You must use only the subset of base class library features which are common to .NET 2 and .NET 4.

Alternatively, you can use the CLR native API's to do this. This is a much more complex solution. The IMetaDataDispenserEx::SetOption api will allow you to set the MetaDataRuntimeVersion. When using the native api - you can't fall back to managed code and use the ReflectionEmit classes. You will need to use the IMetaDataEmit interface to create the assembly metadata and then use the ICeeFileGen interface to create the assembly and its IL. The native interfaces give you the flexibility of changing the framework target dynamically while using the config file is a static solution.

IMetaDataDispenserEx::SetOption - msdn.microsoft.com/en-us/library/ms231869.aspx

ICeeFileGen Class - msdn.microsoft.com/en-us/library/ms404463.aspx

Attached is a sample that demonstrates the static config file method. It contains a TestAssemBldr project to generate a dynamic assembly with .NET 4 and a Loader project that loads the assembly in .NET 2. The App.config in TestAssmBldr is set to generate v2 metadata - comment out the requiredRuntime attribute to generate the BadImageFormatException.

 

ReflectEmitConfigFile.zip