Generating a Portable Class Library through Reflection.Emit
Asked Answered
A

1

9

I am writing a compiler which generates on-disk .NET assemblies using the System.Reflection.Emit API. The compiler itself is built against .NET 4.5, but the generated code only references types from Portable Class Libraries. However, when trying to reference a generated assembly from a Windows Phone 8 project, Visual Studio complains that A reference to a higher version or incompatible assembly cannot be added to the project.

When opening the generated assembly in a decompiler, I can see that it references two PCLs plus mscorlib 4.0.0.0, whereas I understand that PCLs are supposed to reference mscorlib 2.0.5.0.

Is there a way to make make the System.Reflection.Emit API generate PCLs, or is my only option to migrate to Mono.Cecil?

Afterdeck answered 20/2, 2014 at 0:15 Comment(3)
You might also want to have a look at IKVM.Reflection, it allows you to target different version of the framework than the current one.Nanna
@Nanna Thanks, I wasn't aware that IKVM had a public Emit API. At first glance, Mono.Cecil seems better designed though. Are there any reasons I should prefer IKVM.Reflection?Afterdeck
The API of IKVM Reflection is intentionally very similar to normal Reflection. So, if you already know that, you should be able to start using it very quickly.Nanna
A
5

All right, I'll answer my own question.

I've found no evidence that the System.Reflection.Emit APIs can generate assemblies referencing another version of mscorlib than the one used used by the current process. Indeed, the APIs that take System.Type parameters and other reflection objects presumably add a reference to the result of querying their Type.Assembly property, which corresponds to the version of mscorlib in use.

However, portable class libraries aren't that different from what System.Reflection.Emit generates, so it is possible to patch the assemblies after the fact to "make them portable". Disclaimer: this requires familiarity with the PE file format and might have unforeseen side-effects, but it works for me:

  • When generating the assembly, use AssemblyBuilder.SetCustomAttribute to add this attribute to the assembly:

    [System.Runtime.Versioning.TargetFrameworkAttribute(".NETPortable,Version=v4.0,Profile=Profile136", FrameworkDisplayName = ".NET Portable Subset")]
    
  • This is where it gets sketchy: after having called AssemblyBuilder.Save, open a read/write file stream to the generated assembly, walk through the PE, COFF, COM, CLI and metadata table headers to locate the AssemblyRef table row for mscorlib. Modify the referenced version of mscorlib to 2.0.5.0, add 0x100 to its flags ("retargetable") and update its public key token blob to 0x7CEC85D7BEA7798E.

Note that if you use other framework assemblies, you might need to patch their references as well (I haven't tested this). Otherwise, voilà! The assembly is now portable and can be used, for example, in a Windows Phone project.

(... or just use Mono.Cecil/IKVM.Reflection ...)

Edit: The code I used can be found on github. This is a huge hack so the usual disclaimers apply, use at your own risks.

Afterdeck answered 24/2, 2014 at 13:51 Comment(5)
Since it looks like you already wrote the code that modifies the assembly, it might make sense for you to share it.Nanna
@Nanna I would, but the thing's a good 300+ lines long, plus a bunch of structs and enums. I'll gladly share it to whoever asks me for it, though... if there is such a thing as PMs on stackoverflow.Afterdeck
You could post it to pastebin.Zombie
Is it justmscorlib that you need to modify the referece? Wouldn't you need to modify all system assemblies referenced?Electromechanical
@Electromechanical Hmm, good point. In my case I'm only using mscorlib so I didn't have to worry about this. I'll update the post.Afterdeck

© 2022 - 2024 — McMap. All rights reserved.