Sunday, October 2, 2016

Supporting .NET 2.0 references in a 4.0+ class library at runtime

By Steve Endow

If you don't know what a .NET mixed mode assembly is, you can skip this post and save a few minutes of your life.  If you do know what a .NET mixed mode assembly is and understand why it can be a pain, you may want to read on.

This post is primarily to document this solution for myself so that I can find it again in the future without having to dig through 10 old projects (like I had to do yesterday).

This solution is something my colleague Andrew Dean and I have shared during our Dynamics GP .NET Development presentations at the reIMAGINE and GPUG Summit, as it is something we have had to use in our development projects with Dynamics GP.

In the case of Dynamics GP, the primary reason I've had to use this is because I regularly use GPConnNet.dll.  That dll is still only .NET 2.0 'compatible' (or whatever the term is).  So if you develop a new solution using .NET 4.5.1, you will get the famous mixed mode assembly 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.

The standard solution to resolve this error is to modify your application config file.  Meaning your app.config / exe.config file.

That works fine if you are developing an EXE, but it won't work if you are developing a class library DLL that will be used by other applications that you can't control.  Like Dynamics GP.

This post by Reed Copsey Jr. provides an alternative, and brilliant, approach to setting the .NET legacy policy at runtime.  This is a life saver for DLL libraries that are referencing resources with multiple .NET versions.

http://reedcopsey.com/2011/09/15/setting-uselegacyv2runtimeactivationpolicy-at-runtime/

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();
    }
}


Go forth and develop mixed mode assembly class libraries.


You can also find him on Google+ and Twitter





No comments: