How To Use a .NET 4 Based DLL From .NET 2 Based Application?

May 30, 2011

13 comments

Introduction

The official answer is you can’t. Even with the In-Process Side by Side execution (SxS) feature, introduced in .NET 4.

The SxS feature was intended to be used when COM is involved. For example, if you got an application that loads plugins, like outlook, and it loads 2 COM plugins, one is using .NET 4 and the other is using .NET 2.0 then it will load two versions of the CLR into the process using the new SxS feature.

What if I simply have a .NET 2 application or DLL that needs to access a .NET 4 DLL?

Personally I’ve encountered two scenarios when I had to solve this problem:

  1. I had a 3rd-party control that would load only in a .NET 3.5 application, but I had to use it in a .NET 4 application.
  2. I wanted to write a plug-in for Windows Live Writer, which must use .NET 2.0, but I needed to use in my plug-in a .NET 4 DLL.

So, what can we do if no COM is involved?

Well, simply add COM to the mixture..

The idea is that you can expose the required classes from your DLL (which uses .NET Framework X) as COM classes (using COM Interop), and then use those classes from your other DLL (which uses .NET Framework Y). Since you are crossing a COM interface, in-process SxS will kick in and work its magic.

Steps to work around the problem

Create a .NET 4 DLL

Suppose we have a .NET 4 DLL which does some .NET 4 functionality. In the attached example our .NET 4 class prints the CLR version, which should be 4. This DLL is compiled with .NET Framework 4.

using System;

namespace Net4Assembly
{
    public class MyClass
    {
        public void DoNet4Action()
        {
            Console.WriteLine("CLR version from DLL: {0}", Environment.Version);
        }
    }
}

 

Create a .NET 2 EXE

Here we create a .NET 2 EXE which will eventually call the .NET 4 DLL, currently all it does is write it’s own CLR version.

using System;

namespace Net2Assembly
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
        }
    }
}

 

Create a .NET 4 to .NET 2 adapter

Here we create a .NET 4 DLL that exposes the same functionality we need from our original .NET 4 DLL only it exposes it in a COM-friendly way. In this example, it only needs to delegate the call to the original implementation, but in more advanced scenarios it should translate the parameters to something more COM friendly. In addition to changing the parameters the classes also implement interfaces (as required by COM) and are marked with ComVisible and Guid attributes to allow access using COM.

Here is our COM visible interface:

using System;
using System.Runtime.InteropServices;

namespace Net4ToNet2Adapter
{
    [ComVisible(true)]
    [Guid("E36BBF07-591E-4959-97AE-D439CBA392FB")]
    public interface IMyClassAdapter
    {
        void DoNet4Action();
    }
}

And our COM visible class, which delegates its calls to the original class.

using System;
using System.Runtime.InteropServices;
using Net4Assembly;

namespace Net4ToNet2Adapter
{
    [ComVisible(true)]
    [Guid("A6574755-925A-4E41-A01B-B6A0EEF72DF0")]
    public class MyClassAdapter : IMyClassAdapter
    {
        private MyClass _myClass = new MyClass();

        public void DoNet4Action()
        {
            _myClass.DoNet4Action();
        }
    }
}

Note: we could have combined Net4Assembly.MyClass and Net4ToNet2Adapter.MyClassAdapter into the same class but I wanted to keep the example general. In real life application you often can’t change the original object and thus you are forced to create a wrapper.

 

Add support to the adapter for registration-free COM activation

Important note: this part is not really necessary for the .NET 4 to .NET 2 interop to work. But without it you will need to start using he registry for registering your .NET COM components and most projects would rather to avoid it if possible. If this is not a problem just register your objects in the registry and move to the next step.

To add support for registration-free COM we need to create two application manifest files.

The first application manifest specifies dependent assemblies for the client executable. Note that since this manifest replaces the default .NET manifest I’ve added some extra standard manifest stuff (trustinfo), but only the first part is really needed for the registration-free COM to work. To add it, add a file to the client project named app.manifest (“Add new item” –> “Application Manifest”) and change the project properties to use this file.

Following is the content of app.manifest for the Net2Aseembly.exe client in our example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly
  xmlns="urn:schemas-microsoft-com:asm.v1"
  manifestVersion="1.0">
  <assemblyIdentity
    type = "win32"
    name = "Net2Assembly"
    version = "1.0.0.0"
    />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Net4ToNet2Adapter"
        version="1.0.0.0" />
    </dependentAssembly>
  </dependency>

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!– UAC Manifest Options
            If you want to change the Windows User Account Control level replace the
            requestedExecutionLevel node with one of the following.

        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            If you want to utilize File and Registry Virtualization for backward
            compatibility then delete the requestedExecutionLevel node.
        –>
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

 

The second application manifest, describes the COM components which are exposed in the assembly. It needs to be set as the application manifest which resides as a native Win32 resource inside the DLL. 

Unfortunately, this can’t be done as easily as the previous manifest. In Visual Studio 2010, the relevant field in the project properties is disabled when the project is of type Class Library. So we must go to the Net4ToNet2Adapter.csproj file and change it ourselves. The change is easy, just add the following lines in the relevant place:

<PropertyGroup>
  <ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

Following is the content of app.manifest for the Net4ToNet2Adapter.dll in our example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="Net4ToNet2Adapter"
    version="1.0.0.0" />
  <clrClass
    clsid="{A6574755-925A-4E41-A01B-B6A0EEF72DF0}"
    progid="Net4ToNet2Adapter.MyClassAdapter"
    threadingModel="Both"
    name="Net4ToNet2Adapter.MyClassAdapter"
    runtimeVersion="v4.0.30319"
    />
</assembly>

 

Use our .NET 4 DLL via COM

Now all you need to do is create an instance of your .NET 4 class from your .NET 2 executable using COM:

using System;
using Net4ToNet2Adapter;

namespace Net2Assembly
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
            
            Type myClassAdapterType = Type.GetTypeFromProgID("Net4ToNet2Adapter.MyClassAdapter");
            object myClassAdapterInstance = Activator.CreateInstance(myClassAdapterType);
            IMyClassAdapter myClassAdapter = (IMyClassAdapter)myClassAdapterInstance;
            myClassAdapter.DoNet4Action();
        }
    }
}

Note: Since the interface IMyClassAdapter should be duplicated in the client, I’ve added the source file IMyClassAdapter.cs as a link to the client project.

 

The result

The result of running this simple console application is:

image

I think the image speaks for itself.

I’ve uploaded the full source of this example to MSDN Code Gallery.

That’s it for now,
Arik Poznanski.

Add comment
facebook linkedin twitter email

Leave a Reply

13 comments

  1. JasonOctober 7, 2011 ב 05:16

    Why doesn’t this seem to work when the Net2Aseembly.exe is a WinForms exe?

  2. Andy KOctober 10, 2011 ב 11:24

    Thanks for a great article – just what I needed. However, I also need to consume events exposed by my .net4 component. I can subscribe to the event but when it is raised I get an exception; “This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server.”

    Any tips would be greatly received!

  3. arikOctober 25, 2011 ב 16:46

    Jason, I can’t see a reason why it shouldn’t work. Can you specify more what exactly did you do, and what went wrong?

    Andy K, Since we’re using COM, the events use COM mechanism events (connection point containers, sinks and other curses), I suggest you implement your own “events mechanism” by providing an object that implements a predefined interface, instead of using standard COM events.
    Hopefully, the idea is clear, if not I can further add details if needed.

  4. ioanaNovember 4, 2011 ב 20:24

    Recently we upgrade our application fron vb 6 to vb.net, for some reasons the application was compiled in .net 2.0 , but now i have to build a new dll using .net 4.0 , the problem i hafe to call a function in .net 2.0 app to got a string connection, my question, can i use the same technique ?

  5. ChandanNovember 23, 2011 ב 11:36

    Hi, i have downloaded the above code, it works fine. However, if i want to call Net4ToNet2Adapter from a .net 2.0 dll (say adapter.dll) and then call adapter.dll from a console program, it does work. The following code

    Type myClassAdapterType = Type.GetTypeFromProgID(“Net4ToNet2Adapter.MyClassAdapter”);

    returns null.

    Can you please help me here?

  6. AxelDJanuary 24, 2012 ב 18:54

    Thanks a lot, Arik! That’s exactly the solution I’m searching for with my Mixed Mode Assembly problem!

    Great job!

  7. AxelDJanuary 24, 2012 ב 19:24

    Hi, Arik,

    I just tried your steps without creating the manifests but using
    RegAsm xxx.dll /TLB /CODEBASE /NOLOGO

    While I can use my DLL in Visual BASIC 6, I can’t add a COM reference to one of my .NET 2.0 projects. This is the error message I get using Visual Studio 2010 SP1:

    “A reference to ‘Test Class’ could not be added.

    The ActiveX type library C:…\TestClass.tlb’ was exported from a .Net
    assembly and can not be added as a reference.

    Add a reference to the .Net assembly instead.”

  8. Eric GreenMay 30, 2012 ב 16:47

    Arik,

    How would you do this same thing, but with a class library/.dll for the 2.0 project? I am not able to figure out how to embed a manifest in the dll.

    Thanks!

  9. PatrickMay 31, 2012 ב 16:50

    I have downloaded the solution and it works well in Visual Studio 2010.

    However, when I try something further, it is broken. What I have tried is that I created a .NET 2.0 class project “Net2Library” to use “Net4ToNet2Adapter” and dynamically load “Net2Library” in “Net2Assembly” project and call a similar function DoNet2Action using:

    Assembly ass = Assembly.LoadFrom(“Net2Library.dll”);
    Type classType = ass.GetType(“Net2Library.Net2Class”);
    MethodInfo methodInfo = classType.GetMethod(“DoNet2Action”);
    object net2ClassObj = Activator.CreateInstance(classType);
    methodInfo.Invoke(net2ClassObj, null);

    public void DoNet2Action()
    {
    Console.WriteLine(“CLR version from EXE: {0}”, Environment.Version);

    Type myClassAdapterType = Type.GetTypeFromProgID(“Net4ToNet2Adapter.MyClassAdapter”);
    object myClassAdapterInstance = Activator.CreateInstance(myClassAdapterType);
    IMyClassAdapter myClassAdapter = (IMyClassAdapter)myClassAdapterInstance;
    myClassAdapter.DoNet4Action();
    }

    However, when it run into the DoNet2Action, I get Type myClassAdapterType to be null.

    Any idea?

  10. Chintan UpadhyayJune 1, 2012 ב 13:49

    Thanks, It really helps to call 4.0 dll in 3.5

  11. PerilJune 5, 2012 ב 03:09

    how i can to pass args from .NET 2 dll to .NET 4 DLL and get results from that

  12. HappenstanceJune 6, 2012 ב 17:59

    As best I can tell, there are a couple of caveats with this method.

    1) Adding the dependency in an app.manifest to a DLL did not work for me. I had to register the adaptor DLL with Windows.

    2) The calling EXE or DLL must be 32-bit to work with COM. http://blogs.msdn.com/b/karthick/archive/2006/02/28/540780.aspx
    Setting it to AnyCPU and running on a 64-bit system results in an error like:

    Failed –Retrieving the COM class factory for component with CLSID {CA0752B3-021C-4F99-82E3-2C0F19C5E953} failed due to the following error: 80040154.

  13. Michael ZJuly 31, 2012 ב 23:57

    Hi! Great idea but your code does not work if the calling app is a WinForm app. It breaks on this line

    object myClassAdapterInstance = Activator.CreateInstance(myClassAdapterType);

    The error is “Retrieving the COM class factory for component with CLSID {…} failed due to the following error: 80070002.”

    Please convert your calling app into a .net 2.0 WinForm exe and you can see by yourself. My exe is nothing but a form with a button.

    Please help.

    Many thanks in advance,

    –Mike