7

I'm trying to find the best performing method of calling into Managed .NET code from Unmanaged C++ code. I have found information on Hosting .NET within my C++ application and I'm able to create a pRuntimeHost and start it without a problem.

The ExecuteInDefaultAppDomain seems very limited since I really want to send it a few parameters and have it return a structure of information. The most obvious alternative is to use COM methods but the current C# code isn't really setup as interfaces with methods.

Either way I want to return integers, strings (char *)s, doubles and other core C++ types. There is too much code on both sides to convert the C++ to C# and using Managed C++ isn't an acceptable solution, since the other groups using this C++ code don't want to start using Managed code for performance reasons.

The goal is modify the existing C++ and C# code as little as possible but still use methods within the C# code at specific points within the C++ without majorly affecting the speed of the C++ code.

Based on code found on the Internet the startup and shutdown sequence to host .NET is:

#include "stdafx.h"
#include <metahost.h>

#pragma comment(lib, "mscoree.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    ICLRMetaHost       *pMetaHost       = NULL;
    ICLRMetaHostPolicy *pMetaHostPolicy = NULL;
    ICLRDebugging      *pCLRDebugging   = NULL;

    HRESULT hr;
    hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
    hr = CLRCreateInstance(CLSID_CLRMetaHostPolicy, IID_ICLRMetaHostPolicy, (LPVOID*)&pMetaHostPolicy);
    hr = CLRCreateInstance(CLSID_CLRDebugging, IID_ICLRDebugging, (LPVOID*)&pCLRDebugging);

    DWORD dwVersion = 0;
    DWORD dwImageVersion = 0;
    ICLRRuntimeInfo *pRuntimeInfo;
    hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID *)&pRuntimeInfo);

    ICLRRuntimeHost * pRuntimeHost = NULL;
    hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *)&pRuntimeHost);

    hr = pRuntimeHost->Start();

    DWORD dwRetCode = 0;
    //hr = pRuntimeHost->ExecuteInDefaultAppDomain(argv[1], L"MyNamespace.MyClass", L"Message", L"Hello World!", &dwRetCode);

    // Stop the CLR runtime and shutdown cleanly.
    hr = pRuntimeHost->Stop();
    hr = pRuntimeHost->Release();
    hr = pRuntimeInfo->Release();
    hr = pCLRDebugging->Release();
    hr = pMetaHostPolicy->Release();
    hr = pMetaHost->Release();

    return 0;
}
Andrew Stern
  • 688
  • 9
  • 18

2 Answers2

6

Yes, I agree with John. You don't really want to create a new instance of the runtime and host it explicitely. First, the plumbing behind this is not well documented and could change in future versions. Second, C++/CLI was designed to do exactly this in most efficient and safe way.

  1. Write native C++ interfaces which represents the required .Net funcionality.

  2. Set up a dll with CLR support which implements the native interfaces using umanaged classes. Inside of their implementation you can create and access CLR types and store instance variables in gcroot<T> fields. Use the clr interop funcionality to marshal back and forth between managed/unmanaged code, google or bing for marshal_as.

  3. Provide an (unmanaged) factory function, which creates an instance of this component. This + the unmanaged c++ interface is the API your native code will see. Use the dll exactly the same way you'd use an unmanaged dll.

Paul Michalik
  • 4,331
  • 16
  • 18
  • The reason for creating an additional AppDomain is that the C++ code already is using the default AppDomain for some stuff and I don't want to have my additional .NET Assemblies interfere with the current code and also to avoid having their stuff interfere with mine. BTW I managed to the the CLI layer working perfectly but I'm still trying to figure out how to get the whole CLI layer into a separate AppDomain that is not the default AppDomain. – Andrew Stern Apr 24 '12 at 19:08
  • Haven't tried to do that myself yet, but theoretically this should not be a problem. I understand your scenario is to call into a managed component from native one, right? There is an feature called "thread promotion" (google or bing) which causes a native thread to be promoted to a managed one, whenever it first tries to execute managed code. Since the CLR has no idea in which AppDomain the managed code called that way should execute, it puts it into the default one. So you will have to explicitly handle that transition, probably using the `msclr::call_in_appdomain` family of functions. – Paul Michalik Apr 26 '12 at 17:47
  • I have documented my final solution in this location: http://stackoverflow.com/questions/10301727/marshalling-c-pointer-interface-back-though-c-sharp-function-call-in-a-non-def – Andrew Stern Jun 05 '12 at 11:29
3

If it is acceptable, the best solution may be to create a managed C++ dll that sits in-between. Managed C++ code is the best/most-efficient way to bridged managed and unmanaged code.

You really don't want to add COM into the mix. That will slow things down much more.

Also, are these "performance reasons" to avoid managed code actually quantified? It sounds kind of like an anecdote thrown out to avoid something they just don't want. Also, you could point out that they are already using managed code, since C# is in the mix.

John Fisher
  • 22,355
  • 2
  • 39
  • 64
  • The performance is being counted in microseconds. So after all this I've added caching of the request/response as a C++ std::map. This allows a quick lookup for anything previously requested without going back to the C# layer. I've attached additional info above. – Andrew Stern Jun 05 '12 at 11:34