An Inside Look at ASP.NET 5 Execution – Part II – Native Host & Managed Entry Point

Side note before diving into the code: following the announcements from Build 2015 I’ve upgraded to VS 2015 RC and ASP.NET 5 and DNX beta 4 (the current stable version). This means I’ll be using the upgraded command line tools with their new names. Upgrading from the thins shown on the previous post is straightforward:

  • Install DNVM (previously KVM) using the command on Git Hub.
  • Change the sample project’s dependencies to beta 4 and update the framework designation on project.json. It now looks like this:
"webroot": "wwwroot",
"dependencies": {
"Microsoft.AspNet.StaticFiles": "1.0.0-beta4",
"Microsoft.AspNet.Mvc": "6.0.0-beta4",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
"Microsoft.AspNet.Hosting": "1.0.0-beta4"
"commands": {
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
"frameworks": {
"dnxcore50": { }

  • Install DNX beta 4:
dnvm install 1.0.0-beta4 -arch x64 -r coreCLR -Persistent
  • Restore project dependencies using dnu (was kpm) on the project’s folder:
dnu restore
  • Start the application:
dnx . web
  • Pull the latest changes from the ASP.NET and DNX repos mentioned on the previous post and checkout the beta4 tag

On Visual Studio, I had to manually restore references for some projects but eventually everything loaded up. Finally, it’s worth checking the latest version of the DNX structure wiki page.

Now, into the code! Let’s start on the DNX itself. I’m not going to build or debug here but just by looking into the code we can get an idea of what’s going on. On the DNX source, there an “host” folder that contains the projects for the CLR Native Hosts and the Managed Entry Point (layers 1 and 2 on the DNX structure diagram).


Accordingly to the documentation, the CLR Native Host (layer 1) is responsible for booting the CLR and calling the Managed Entry Point. The actions undertaken to boot the CLR depend on the version of the CLR. “For Core CLR the process involves loading coreclr.dll, configuring and starting the runtime, and creating the AppDomain that all managed code will run in”. The following excerpts were taken from the “dnx.coreclr\dnx.coreclr.cpp” file, and highlight the aforementioned steps.

HMODULE hCoreCLRModule = LoadCoreClr(); // (1)
// ...
pfnGetCLRRuntimeHost = (FnGetCLRRuntimeHost)::GetProcAddress(hCoreCLRModule, "GetCLRRuntimeHost");
// ...
hr = pfnGetCLRRuntimeHost(IID_ICLRRuntimeHost2, (IUnknown**)&pCLRRuntimeHost);
// ...
// ...
hr = pCLRRuntimeHost->Start(); // (2)
// ...
hr = pCLRRuntimeHost->CreateAppDomainWithManager(
&domainId); // (3)

As previously described, the first thing to do is load the CoreCLR DLL (1). Then, the native host is created and started (2), allowing the creation of the AppDomain that will execute the managed code (3).

Having the app domain, DNX creates (4) and executes (5) a delegate for the managed entry point, which is implemented by the Execute method of dnx.coreclr.managed.DomainManager. When the managed code execution returns, the AppDomain is unloaded and the native host stopped (6).

LPCWSTR szAssemblyName = L"dnx.coreclr.managed, Version=";
LPCWSTR szEntryPointTypeName = L"DomainManager";
LPCWSTR szMainMethodName = L"Execute";
HostMain pHostMain;

hr = pCLRRuntimeHost->CreateDelegate(
(INT_PTR*)&pHostMain); // (4)

SetEnvironmentVariable(L"DNX_FRAMEWORK", L"dnxcore50");

// Call main
data->exitcode = pHostMain(data->argc, data->argv); // (5)

pCLRRuntimeHost->UnloadAppDomain(domainId, true); // (6)

The managed execution (layer 2) starts on the DomainManager class which just packs the arguments and invokes the RuntimeBootstrapper class ( project). This class determines the assembly search paths and forwards execution to the Bootstrapper class. Here are the managed entry point responsibilities of “creating the LoaderContainer that will contain the required ILoaders(1), create an ILoader for DNX libraries (2) and “call the main entry point of the provided program(4).

// ...
var container = new LoaderContainer(); // (1)
// ...
var disposable = container.AddLoader(new PathBasedAssemblyLoader(accessor, _searchPaths)); // (2)
var name = args[0];
var programArgs = new string[args.Count - 1];
args.CopyTo(1, programArgs, 0, programArgs.Length);

var assembly = Assembly.Load(new AssemblyName(name));
// ...
var serviceProvider = new ServiceProvider();
serviceProvider.Add(typeof(IAssemblyLoaderContainer), container); // (3)
// ...
var task = EntryPointExecutor.Execute(assembly, programArgs, serviceProvider); // (4)

It’s worth pointing out that this is where the intrinsic dependency injection classes are used for the first time to register some core services (3). It’s also worth peaking into EntryPointExecutor which contains the logic to find a Main method on the provided assembly, with support for both static and non-static classes.

var programType = assembly.GetType("Program") ?? assembly.GetType(name + ".Program");
// ...
entryPoint = programType.GetTypeInfo().GetDeclaredMethods("Main").FirstOrDefault();
// ...
instance = programType.GetTypeInfo().IsAbstract ? null : ActivatorUtilities.CreateInstance(serviceProvider, programType);

Note that he “provided program” could be an application compiled to assemblies on disk and invoked directly by the bootstrapper. However, on our case this program is the generic application host provided by the runtime (Microsoft.Framework.ApplicationHost). I’ll continue from there on the following post!


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s