using System; namespace Net4Assembly { public class MyClass { public void DoNet4Action() { Console.WriteLine("CLR version from DLL: {0}", Environment.Version); } } }
using System; namespace Net4Assembly { public class MyClass { public void DoNet4Action() { Console.WriteLine("CLR version from DLL: {0}", Environment.Version); } } }
using System; namespace Net2Assembly { class Program { static void Main(string[] args) { Console.WriteLine("CLR version from EXE: {0}", Environment.Version); } } }
using System; namespace Net2Assembly { class Program { static void Main(string[] args) { Console.WriteLine("CLR version from EXE: {0}", Environment.Version); } } }
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(); } }
using System; using System.Runtime.InteropServices; namespace Net4ToNet2Adapter { [ComVisible(true)] [Guid("E36BBF07-591E-4959-97AE-D439CBA392FB")] public interface IMyClassAdapter { void DoNet4Action(); } }
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(); } } }
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(); } } }
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>
<?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>
<PropertyGroup> <ApplicationManifest>app.manifest</ApplicationManifest> </PropertyGroup>
<PropertyGroup> <ApplicationManifest>app.manifest</ApplicationManifest> </PropertyGroup>
<?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>
<?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>
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(); } } }
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(); } } }
There you go, two CLR versions in the same process!