Host the CLR and Generate IL to call a MessageBox

Here’s some C++ code to host the CLR. It’s an alternative to using COM Interop (see A Visual Basic COM object is simple to create, call and debug from Excel), or using a User Control (see Create a .Net UserControl that calls a web service that acts as an ActiveX control to use in Excel, VB6, Foxpro)

I created a C++ native project in a Visual Studio Solution and added a method called “foobar” which uses COM to start the CLR using CorBindToRuntimeEx. It then loads an assembly and calls the “EntryPoint” method, with a single string parameter.

I added a VB.Net Class Library project into the VS Solution which created an assembly (d:\dev\vb\CLRHostTest\bin\debug\CLRHostTest.dll) that has a static method called “EntryPoint” that takes a single string parameter. The method dynamically generates (using System.Reflection.Emit) an assembly called “testasm” with a module called “MyModule”, which has a public class called “FoobarType” and a static method called “Main”. The code in “Main” shows a messagebox of the parameter passed to “EntryPoint”

Quite a bit of work just to show a MessageBox!

#include "assert.h"

#include <mscoree.h>

#import <mscorlib.tlb> raw_interfaces_only rename("ReportEvent","ReportEventManaged") //high_property_prefixes("_get","_put","_putref")

using namespace mscorlib;

void foobar() {

      HRESULT hr;

      CComPtr<ICLRRuntimeHost> pHost = NULL;

      hr = CorBindToRuntimeEx(L"v2.0.50727",L"wks",0 //STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN | STARTUP_CONCURRENT_GC

            ,CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void **)&pHost);

      _ASSERT(hr == S_OK);

      hr = pHost->Start();

      _ASSERT(hr == S_OK);

      DWORD retVal;

      hr = pHost->ExecuteInDefaultAppDomain(L"d:\\dev\\vb\\CLRHostTest\\bin\\debug\\CLRHostTest.dll",

      L"CLRHostTest.CLRHostTestClass",L"EntryPoint",L"System.Windows.Forms.MessageBox.Show(\"test\")",&retVal);

      _ASSERT(hr == S_OK);

      pHost->Stop();

      exit(0);

}

Here’s the VB code that gets called:

Imports System.Reflection

Imports System.Reflection.Emit

Public Class CLRHostTestClass

    Shared Function EntryPoint(ByVal cArg As String) As Integer

        ' MsgBox("here in CLRHostTest " & cArg)

        Dim asmName As AssemblyName = New AssemblyName("testasm")

        Dim appDomain As AppDomain = System.Threading.Thread.GetDomain

        Dim asmBldr As AssemblyBuilder = appDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run)

        Dim moduleBldr As ModuleBuilder = asmBldr.DefineDynamicModule("MyModule")

        Dim typeBldr As TypeBuilder = moduleBldr.DefineType("FoobarType", TypeAttributes.Class + TypeAttributes.Public)

        Dim methodBldr As MethodBuilder = typeBldr.DefineMethod("Main", MethodAttributes.Public + MethodAttributes.Static, _

            GetType(Integer), New System.Type() {GetType(String)})

        Dim ilg As ILGenerator = methodBldr.GetILGenerator()

        Dim msgboxMethodInfo As Reflection.MethodInfo = GetType(System.Windows.Forms.MessageBox).GetMethod( _

            "Show", BindingFlags.Public + BindingFlags.Static, Nothing, _

            CallingConventions.Standard, New System.Type() {GetType(String)}, Nothing)

        ilg.Emit(OpCodes.Ldstr, cArg)

        ilg.EmitCall(OpCodes.Call, msgboxMethodInfo, Nothing)

        ilg.Emit(OpCodes.Ret)

        Dim mytype As Type = typeBldr.CreateType()

        mytype.InvokeMember("Main", BindingFlags.Public + BindingFlags.InvokeMethod + BindingFlags.Static _

            , Nothing, Nothing, New String() {"aa"})

  Return 10

    End Function

End Class

Another way to call the method using mixed mode managed C++ (compiling with /clr)

#using "d:\dev\vb\CLRHostTest\bin\debug\CLRHostTest.dll"

void foobar()

{

   System::String^ s = "Some text";

   CLRHostTestClass::EntryPoint(s);

}

BTW, it was very hard to find documentation on the “^” (circumflex or caret) character. Try typing it into a search engine! Here’s a link: Handle to Object on Managed Heap