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

Comments

  • Anonymous
    August 07, 2006
    PingBack from http://www.robherbst.com/blog/2006/08/07/host-the-clr-in-c/

  • Anonymous
    August 08, 2006
    The comment has been removed

  • Anonymous
    August 08, 2006
    So how would this be more benficial than say doing COM interop into the CLR which automatically creates a default AppDomain?

    I can see this being useful for pure C++ code, but I'm not sure I see the immediate benefit for doing this higher up the stack for say FoxPro or VB 6 since you still need to pass data across somehow. I don't think that there's a big performance gain either when you compare COM interop vs. directly calling into the CLR.

    Can you give a use case when this would be useful?

    I'm just thinking out loud here. A while back I built an Add-in interface into Help Builder for example, that allows Add-in creation in .NET even though the application is a FoxPro application. It uses COM interop and that works just fine. i just don't see the benefit of a 'pure' .NET hosting situation unless you need to have a primary gateway (ie. IIS, IE etc.).

  • Anonymous
    December 18, 2006
    Hi Calvin,   Thanks for the information. I've tried doing the same way to use my managed c++ code in unmanaged c++ . But the API ExecuteInDefaultAppDomain(..) fails. May I know why is it so? Regards, RSL

  • Anonymous
    November 04, 2008
    <a href= http://index1.ystins.com >laguardia rising stars 2006</a> <a href= http://index2.ystins.com >northumberland county va</a> <a href= http://index3.ystins.com >holy ghost prep bensalem craft</a> <a href= http://index4.ystins.com >miles away from the ordinary</a> <a href= http://index5.ystins.com >star trader software</a>