Your New Jekyll Site

home

Clr Hosting API

26 May 2012

The CLR Hosting API is a great utility for people interested in customizing their .Net applications beyond what is possible through the managed API.

The API let you replace the garbage collector & memory handler, handle system-wide exceptions, override types, even force your application to use a specific version of the CLR runtime. Native C/C++ applications, can use the CLR Hosting API to make it a mixed mode application at run-time that can create new app domains and execute .Net code.

It is for example possible to list installed .Net runtimes, then based on the result, start a specific version of the .Net runtime. The API is a meta API, meaning that apart from running .Net code it is also possible to query the .Net runtime about loaded modules, exceptions, replace memory handlers, insert global exception handlers, restart app domains when they crash, etc.

Below are code snippets for enumerating the installed .Net runtime versions, and how to force a specific version of the .Net run-time to be loaded, meaning that you can launch assemblies compiled for the v3.5 runtime in the v4.0 runtime, without having to install version 3.5. You can also prevent assemblies from using patches and updates that you haven’t tested your software with.

Enumerating Installed .Net Runtimes

#include <windows.h>
#include <mscoree.h>
#include <string>
#include <list>
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")

HRESULT GetInstalledClrRuntimes(std::list<std::wstring>& clrRuntimeList)
{
  HRESULT hr = S_OK;        
  clrRuntimeList.clear();
  ICLRMetaHost* metahost = nullptr;
  hr = CLRCreateInstance(CLSID_CLRMetaHost,
                         IID_ICLRMetaHost,
                         (LPVOID*)&metahost);
  if (FAILED(hr))
    return hr;

  IEnumUnknown* runtimeEnumerator = nullptr;
  hr = metahost->EnumerateInstalledRuntimes(&runtimeEnumerator);
  if (SUCCEEDED(hr))
  {
    WCHAR currentRuntime[50];
    DWORD bufferSize = ARRAYSIZE(currentRuntime);
    IUnknown* runtime = nullptr;
    while (runtimeEnumerator->Next(1, &runtime, NULL) == S_OK)
    {
      ICLRRuntimeInfo* runtimeInfo = nullptr;
      hr = runtime->QueryInterface(IID_PPV_ARGS(&runtimeInfo));
      if (SUCCEEDED(hr))
      {
        hr = runtimeInfo->GetVersionString(currentRuntime, &bufferSize);
        if (SUCCEEDED(hr))
        {
          clrRuntimeList.push_back(std::wstring(currentRuntime));
        }
        runtimeInfo->Release();
      }
      runtime->Release();
    }
    runtimeEnumerator->Release();
    hr = S_OK;
  }
  metahost->Release();
  return hr;
}

Starting/Hosting a .Net runtime inside a C/C++ application.

ICLRMetaHost* m_metahost = nullptr;
ICLRRuntimeInfo* m_runtimeInfo = nullptr;

HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost,
                               IID_ICLRMetaHost,
                               (LPVOID*)&m_metahost);
hr = m_metahost->GetRuntime(_T("v4.0.30319"),
                               IID_ICLRRuntimeInfo,
                               (LPVOID*)&m_runtimeInfo);