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
?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With