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