Setting useLegacyV2RuntimeActivationPolicy At Runtime
Version 4.0 of the .NET Framework included a new CLR which is almost entirely backwards compatible with the 2.0 version of the CLR. However, by default, mixed-mode assemblies targeting .NET 3.5sp1 and earlier will fail to load in a .NET 4 application. Fixing this requires setting useLegacyV2RuntimeActivationPolicy in your app.Config for the application. While there are many good reasons for this decision, there are times when this is extremely frustrating, especially when writing a library. As such, there are (rare) times when it would be beneficial to set this in code, at runtime, as well as verify that it’s running correctly prior to receiving a FileLoadException.
Typically, loading a pre-.NET 4 mixed mode assembly is handled simply by changing your app.Config file, and including the relevant attribute in the startup element:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/> </startup> </configuration>
This causes your application to run correctly, and load the older, mixed-mode assembly without issues. For full details on what’s happening here and why, I recommend reading Mark Miller’s detailed explanation of this attribute and the reasoning behind it.
Before I show any code, let me say:
- I strongly recommend using the official approach of using app.config to set this policy.
That being said, there are (rare) times when, for one reason or another, changing the application configuration file is less than ideal.
While this is the supported approach to handling this issue, the CLR Hosting API includes a means of setting this programmatically via the ICLRRuntimeInfo interface. Normally, this is used if you’re hosting the CLR in a native application in order to set this, at runtime, prior to loading the assemblies. However, the F# Samples include a nice trick showing how to load this API and bind this policy, at runtime. This was required in order to host the Managed DirectX API, which is built against an older version of the CLR.
This is fairly easy to port to C#. Instead of a direct port, I also added a little addition – by trapping the COM exception received if unable to bind (which will occur if the 2.0 CLR is already bound), I also allow a runtime check of whether this property was setup properly:
public static class RuntimePolicyHelper { public static bool LegacyV2RuntimeEnabledSuccessfully { get; private set; } static RuntimePolicyHelper() { ICLRRuntimeInfo clrRuntimeInfo = (ICLRRuntimeInfo)RuntimeEnvironment.GetRuntimeInterfaceAsObject( Guid.Empty, typeof(ICLRRuntimeInfo).GUID); try { clrRuntimeInfo.BindAsLegacyV2Runtime(); LegacyV2RuntimeEnabledSuccessfully = true; } catch (COMException) { // This occurs with an HRESULT meaning // "A different runtime was already bound to the legacy CLR version 2 activation policy." LegacyV2RuntimeEnabledSuccessfully = false; } } [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("BD39D1D2-BA2F-486A-89B0-B4B0CB466891")] private interface ICLRRuntimeInfo { void xGetVersionString(); void xGetRuntimeDirectory(); void xIsLoaded(); void xIsLoadable(); void xLoadErrorString(); void xLoadLibrary(); void xGetProcAddress(); void xGetInterface(); void xSetDefaultStartupFlags(); void xGetDefaultStartupFlags(); [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] void BindAsLegacyV2Runtime(); } }
Using this, it’s possible to not only set this at runtime, but also verify, prior to loading your mixed mode assembly, whether this will succeed.
In my case, this was quite useful – I am working on a library purely for internal use which uses a numerical package that is supplied with both a completely managed as well as a native solver. The native solver uses a CLR 2 mixed-mode assembly, but is dramatically faster than the pure managed approach. By checking RuntimePolicyHelper.LegacyV2RuntimeEnabledSuccessfully at runtime, I can decide whether to enable the native solver, and only do so if I successfully bound this policy.
There are some tricks required here – To enable this sort of fallback behavior, you must make these checks in a type that doesn’t cause the mixed mode assembly to be loaded. In my case, this forced me to encapsulate the library I was using entirely in a separate class, perform the check, then pass through the required calls to that class. Otherwise, the library will load before the hosting process gets enabled, which in turn will fail.
This code will also, of course, try to enable the runtime policy before the first time you use this class – which typically means just before the first time you check the boolean value. As a result, checking this early on in the application is more likely to allow it to work.
Finally, if you’re using a library, this has to be called prior to the 2.0 CLR loading. This will cause it to fail if you try to use it to enable this policy in a plugin for most third party applications that don’t have their app.config setup properly, as they will likely have already loaded the 2.0 runtime.
As an example, take a simple audio player. The code below shows how this can be used to properly, at runtime, only use the “native” API if this will succeed, and fallback (or raise a nicer exception) if this will fail:
public class AudioPlayer { private IAudioEngine audioEngine; public AudioPlayer() { if (RuntimePolicyHelper.LegacyV2RuntimeEnabledSuccessfully) { // This will load a CLR 2 mixed mode assembly this.audioEngine = new AudioEngineNative(); } else { this.audioEngine = new AudioEngineManaged(); } } public void Play(string filename) { this.audioEngine.Play(filename); } }
Now – the warning:
This approach works, but I would be very hesitant to use it in public facing production code, especially for anything other than initializing your own application. While this should work in a library, using it has a very nasty side effect: you change the runtime policy of the executing application in a way that is very hidden and non-obvious.
I think useLegacyV2RuntimeActivationPolicy=”false” should be rather useLegacyV2RuntimeActivationPolicy=”true” in app.Config file
sorry for wrong comments, I have bad day…
Thanks joovie – I corrected it. I had set it to false explicitly for testing this technique, and copy/pasted from the wrong project file… Fixed now.
Hi,
I have a typical situation. I have a project that is using the mixed mode dll. I have set the option useLegacyV2RuntimeActivationPolicy = “true” in the App.config file. Now I have a set up project which is using primary output from my dll having useLegacyV2RuntimeActivationPolicy = “true” . When I install the setup, I am getting the error
Mixed mode assembly is built against version ‘v2.0.50727′ of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.
Is there a way to change the configuration or adding a configuration to the set up file so that I can fix this error in my setup.
Any help greatly appreciated.
Thanks in advance
Rushi,
The problem is that you need to make the application that’s running set this, not your DLL. I’m not sure there’s a good way to do this for a setup project, short of moving your DLL into an executable and launching it. This post’s technique may or may not work, but it could be worth testing in this type of scenario.