Search code examples
c#.netvbaruntimeclr

How to call .NET methods from Excel VBA?


I found a way to call .NET 2 code directly from VBA code:

Dim clr As mscoree.CorRuntimeHost
Set clr = New mscoree.CorRuntimeHost
clr.Start
Dim domain As mscorlib.AppDomain
clr.GetDefaultDomain domain
Dim myInstanceOfDotNetClass As Object
Set myInstanceOfDotNetClass = domain.CreateInstanceFrom("SomeDotNetAssembly.dll", "Namespace.Typename").Unwrap
Call myInstanceOfDotNetClass.ExecuteSomeDotNetMethod

I added references to mscoree.tlb and mscorlib.tlb to Excel VBA using Tools -> References.

This works for .NET CLR 2 assemblies, up to .NET framework version 3.5.

I need to make it work with .NET 4.

I understood that .NET CLR4 introduced another, version agnostic, way of creating an instance of the runtime and I have found a code example written in C++: http://dev.widemeadows.de/2014/02/04/hosting-the-net-4-runtime-in-a-native-process/

My Excel VBA skills are not enough to translate those few lines of code.


Solution

  • The default policy is preventing the CLR 4 from excuting the legacy code from the CLR 2 :

    Set clr = New mscoree.CorRuntimeHost
    

    To enable the legacy execution, you can either create the file excel.exe.config in the folder where excel.exe is located:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0"/>
      </startup>
    </configuration>
    

    Or you can call the native function CorBindToRuntimeEx instead of New mscoree.CorRuntimeHost :

    Private Declare PtrSafe Function CorBindToRuntimeEx Lib "mscoree" ( _
        ByVal pwszVersion As LongPtr, _
        ByVal pwszBuildFlavor As LongPtr, _
        ByVal startupFlags As Long, _
        ByRef rclsid As Long, _
        ByRef riid As Long, _
        ByRef ppvObject As mscoree.CorRuntimeHost) As Long
    
    Private Declare PtrSafe Function VariantCopy Lib "oleaut32" (dest, src) As Long
    
    
    ''
    ' Creates a .Net object with the CLR 4 without registration.  '
    ''
    Function CreateInstance(assembly As String, typeName As String) As Variant
      Const CLR$ = "v4.0.30319"
    
      Static domain As mscorlib.AppDomain
      If domain Is Nothing Then
        Dim host As mscoree.CorRuntimeHost, hr&, T&(0 To 7)
        T(0) = &HCB2F6723: T(1) = &H11D2AB3A: T(2) = &HC000409C: T(3) = &H3E0AA34F
        T(4) = &HCB2F6722: T(5) = &H11D2AB3A: T(6) = &HC000409C: T(7) = &H3E0AA34F
    
        hr = CorBindToRuntimeEx(StrPtr(CLR), 0, 3, T(0), T(4), host)
        If hr And -2 Then err.Raise hr
    
        host.Start
        host.GetDefaultDomain domain
      End If
    
      VariantCopy CreateInstance, domain.CreateInstanceFrom(assembly, typeName).Unwrap
    End Function