Hosting .NET Core Clr in your own process

Tags: .NET Core

.NET Core is a new cloud optimized .NET Runtime developed by Microsoft. It has been announced and published a few months ago. The target of this new runtime is to provide a modular development stack for .NET applications which works across different platforms. If you haven't already heard about it I can recommend this blog post from the .NET Framework Blog.

As soon as .NET Core is released it will bee available as standalone nuget package. Currently it is available as release candidate at the aspnetvnext package feed located at myget.org.

How .NET Core Clr can be used

If you download the nuget package you get a bunch of native dlls and .net assemblies. Within all this files you can find the "coreclr.dll" which is the main dll of the .NET Core Clr. This file contains the complete common language runtime required to execute intermediate language. The coreclr.dll itself is implemented in native language and is always compiled for a specific platform (x86/amd64, Windows/Linux, etc.). Within the coreclr.dll we can find the ICLRRuntimeHost2 interface which we can use to start the core clr and run managed code. Because the corecrl.dll is native we also have to write a native application to access this interface and host the clr.

How to run .NET Code with Core Clr

The following code snipptes show a way how you can user Core Clr in you application. All of the following snippets are wirten in C/C++. To be able use the native interface of Core Clr (mainly ICLRRuntimeHost2) we have to include the "mscoree.h" header file into our project (because Core Clr is not yet published or made open source, what Microsoft promised to do, the easiest way to get the header file is to grap it form the KRuntime of the aspnetvnext project).

#include "mscoree.h"

The first step required to load and run .NET code using Core Clr is to load the Core Clr itself into our process. To do this under windows you can use the "LoadLibraryEx" function. After this we need to create an instance to the ICLRRuntimeHost2 interface. Microsoft provides a factory function to create this instance. The factory function can be retrieved by its name "GetCLRRuntimeHost". The following snippet shows ho to load Core CLR and create an instance to the ICLRRuntimeHost2 interface.

// Load the Core Clr dll into the process
HMODULE hCoreCLRModule = ::LoadLibraryExW(L"<Path to coreclr.dll>", NULL, 0);

// Get the factory function to create and instance of a runtime host
FnGetCLRRuntimeHost pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddres(hCoreCLRModule, "GetCLRRuntimeHost");

// Declare pointer to runtime host
ICLRRuntimeHost2* pCLRRuntimeHost = nullptr;

// Call the factory function to create a new instance of a runtime host
pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&pCLRRuntimeHost);

The next stept is to authenticate the runtime and start it. Authentication is required, otherwise the runtime will not work. I use the same value as Microsoft in the KRuntime (I hope we get a documentation on this soon). The following snippet shows how to authenticate and start the runtime.

// Authenticate the runtime to the host
pCLRRuntimeHost->Authenticate(CORECLR_HOST_AUTHENTICATION_KEY);

// Start the runtime
pCLRRuntimeHost->Start();

Now that the Core Clr is started we can provide some information specific to our .NET Code we want to run. This is done via two string arrays. The first array describes which information we provide, the second contains the concrete values. Think of it as a .NET dictionary. The values we have to provide are the

  • "APPBASE" - Base path of the application, think of it as the execution directory of the application
  • "TRUSTED_PLATFORM_ASSEMBLIES" - A list of paths to assembly files which will be fully trusted by the runtime. Here we have to list all assemblies which have unsafe .NET Code blocks or use PInvode for interop because those assemblies are only executed if they are fully trusted. Separate the file paths with a semicolon. The file paths need to be absolute.
  • "APP_PATHS" - The paths where the Core Clr looks for referenced assemblies to load. Separate the file paths with a semicolon. The file paths need to be absolute.

See the following code snippet on how to set up those settings:

// The properties we provide to the runtime
const wchar_t* property_keys[] =
{
   L"APPBASE",
   L"TRUSTED_PLATFORM_ASSEMBLIES",
   L"APP_PATHS",
};

// The concrete values of the properties
const wchar_t* property_values[] = {
   // APPBASE
   L"<Path to application base path>",
   // TRUSTED_PLATFORM_ASSEMBLIES
   L"<List of full trusted assemblies with full path separated by semicolon>",
   // APP_PATHS
   L"<List of full paths where to look for referenced assemblies>"
};

// Calculate the number of elements in the array int nprops = sizeof(property_keys) / sizeof(wchar_t*);

To apply the settings to the runtime we have to call the "CreateAppDomainWithManager" method. This method takes the settings and the name of the assembly which contains the entry point of our .NET code. The method then creates a new app domain with the settings applied and returns the id of the created app domain.

// Variables required for app domain
DWORD domainId;
DWORD appDomainFlags = APPDOMAIN_ENABLE_PLATFORM_SPECIFIC_APPS | APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP;

// Create an app domain with the provided settings
pCLRRuntimeHost->CreateAppDomainWithManager(
   pCoreClrStartupParams->EntryPointAssemblyName.c_str(),
   appDomainFlags,
   NULL,
   NULL,
   nprops,
   property_keys,
   property_values,
   &domainId);

Now that we have an app domain with our .NET assembly loaded we can search in this app domain and all loaded assemblies for static methods. To do this we can use the "CreateDelegate" method. This method takes the assembly name, type name and method name to search for. If it finds the specified method on the specified type it returns a pointer to that method. Be sure to provide a delegate type which matches the signature of the method you want to search for in you managed code. In the example below I declared a pointer to a method which taks no arguments and returns void.

This is the declaration of my "delegate" in C (in C it is just a pointer to a function):

// Declare a pointer matching the signature of the method
// we want to create a delegate for
typedef void (STDMETHODCALLTYPE *PManagedEntryPoint)();

This is the code required to create the "delegate".

// Declare a variable pointing to our managed method.
PManagedEntryPoint pManagedEntryPoint;
// Create a delegate to the managed entry point pCLRRuntimeHost->CreateDelegate( domainId, L"<Assembly Name>" L"<Type Name>"), L"<Method Name>", (INT_PTR*)&pManagedEntryPoint);

Now we have a pointer to a function pointing to a method in our managed code. We can now just call this function to start the execution of our managed code. Nothing easier than than, see the following line:

// Run the managed method
pManagedEntryPoint();

The previous line executed our managed code. To shut down the Core Clr and free all resource you should run the following lines of code:

// Unload the app domain
pCLRRuntimeHost->UnloadAppDomain(domainId, true);

// Stop the runtime host
pCLRRuntimeHost->Stop();

Be aware that all shown snipptes are just examples. All methods of the ICLRRuntimeHost2 interface return an HRESULT. You shuld check this return value after each method call to ICLRRuntimeHost2.

Hope it helps!

Update

I developed a custom Core Clr host and published it on github. You can find it here.

7 Comments

  • Hot Skynet said

    Excellent stuff. I had been wondering why I have not heard anything regarding the Core CLR. All the current articles only talk about the Core managed libraries. Now I realize that they have not released the CLR code yet.

    I am confused about a couple things though:

    1. Does this mean that there will be a separate in app copy of the CLR for all deployed applications? (Seems inefficient)
    2. What will be of the current framework package for laymen (the average Joe) who needs the .Net Framework for legacy .net applications?

  • Daniel said

    Hi Hot Skynet,

    thanks for your comment.

    To you first question:
    Yes and no. Each app which uses Core Clr can deploy the Core Clr with the app like any nuget package. You have also the option to deploy the Core Clr once and use it by different apps. But no matter how you deploy Core Clr, an app using Core Clr will always load and run the Core Clr in its own process. The Core Clr is designed to be very lightweight so that the footprint in memory and cpu usage will be as small as possible. So I think we don't need to have concerns that our apps will become inefficient as you suppose.

    To you second question:
    You keep going with the full .NET Framework. Core Clr is just an option. Both runtimes are alternatives you can choose from. In practice I think not lots of existing .NET Framework apps will be ported to Core Clr. Foremost I see it as an option for new projects. If you need legacy stuff linke Win Forms for example, there is no choise because those legacy parts of the full .NET Framework will never be ported to Core Clr. In those cases you have to stay with the full .NET Framework.

    Does this help?

  • Phoenix SEO said

    Very good website you have here but I was
    wondering if you knew of any discussion boards that
    cover the same topics discussed in this article?
    I'd really like to be a part of community where I can get suggestions
    from other knowledgeable individuals that share the same interest.
    If you have any recommendations, please let me know.
    Thanks!

  • Daniel said

    Hi Phoenix SEO,
    I can always recommend the .NET Foundation Forums you can find here:
    http://forums.dotnetfoundation.org/top
    Besides of this you can always have a look to the GitHub projects and the issue threads there. For .NET Core you can find this here:
    https://github.com/dotnet/coreclr/issues
    By the way I am always happy to hear the thoughts of others on my blog. Share your opinion to my latest post you can find here:
    http://www.fancy-development.net/changes-in-the-net-ecosystem
    If you need more specific Information don't hesitate to contact me via E-Mail.
    Regards, Daniel

Add a Comment