.NET Framework предоставляет большое количество средств взаимодействия управляемого и неуправляемого кода, включая COM Interop и P/Invoke. Существует много материалов, посвящённых данному вопросу. Однако, о такой возможности, как запуск CRL в неуправляемом процессе посредством CLR Hosting API, написано сравнительно немного. Запуск среды выполнения в неуправляемом приложении может быть полезен в следующих случаях:

  • поддержка расширений, написанных на управляемых языках;
  • создание навесных защит для управляемых приложений;
  • управление работой CLR;
  • сложные случаи отладки;
  • постепенный перевод большого неуправляемого приложения на .NET.

Например, хостинг CLR используется в приложениях Microsoft Office (управляемые надстройки) и Microsoft SQL Server (CLR Stored Procedures).

Развитие CLR Hosting API



.NET Framework 1.x

В первой версии CLR Hosting API представлен интерфейс ICorRuntimeHost, который можно получить при помощи статических функций CorBindTo* (например, CorBindToRuntieEx) библиотеки MSCorEE.dll.

.NET Framework 2.0

В этой версии многие аспекты хостинга CLR перенесены из неуправляемого в управляемый код (программист может управлять хостингом при помощи наследника класса AppDomainManager). Появился новый интерфейс ICLRRuntimeHost.

.NET Framework 4.0 и In-Process Side-by-Side

Предыдущие версии CLR Hosting API имели важное ограничение – было невозможно запустить более одной версии CLR в одном процессе. Это означает, что если есть две надстройки, написанные с использованием разных версий CLR, то, как минимум одну из них придется запускать не под той версией исполняющей среды, для которой она разрабатывалась. Начиная с .NET Framework 4.0 появилась возможность загрузить нескольких версий CLR в одном процессе, при этом одновременно смогут работать сколько угодно версий исполняющей среды, начиная c 4.0 и одна из старых версий (CLR1.1 или CLR2.0). Старые статические функции (CorBindTo*) были объявлены устаревшими, вместо них предлагается использовать функцию CLRCreateInstance и методы интерфейсов ICLRMetaHost и ICLRRuntimeInfo.

Управление работой CLR


В CLR Hosting API можно выделить две большие группы интерфейсов:
  • интерфейсы хост-диспетчеров (host managers), их имена начинаются с префикса IHost, они реализуются на стороне неуправляемого приложения и вызываются CLR;
  • интерфейсы диспетчеров CLR (CLR managers), которые реализуются CLR и вызываются на стороне неуправляемого приложения.

При помощи этих интерфейсов можно контролировать такие аспекты работы CLR, как:
  • работа с памятью (ICLRGCManager, ICLRMemoryNotificationCallback, IHostGCManger, IHostMAlloc, IHostMemoryManager);
  • работа с потоками(ICLRTaskManger, IHostTaskManger, IHostThreadPoolManger);
  • синхронизация (ICLRSyncManger, IHostCrst, IHostSyncManger, IHostManualEvent);
  • загрузка сборок (ICLRAssemblyIdentityManger, IHostAssemblyStore, IHostAssemblyManger);
  • настройка CLR (ICLRControl, ICLRPolicyManger, ICLRDebugManger).

Пример использования CLR Hosting API


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

int wmain( int argc, wchar_t** argv )
{
    ICLRMetaHost              *pMetaHost = nullptr;
    ICLRRuntimeInfo         *pCLRRuntimeInfo = nullptr;
    ICLRRuntimeHost         *pCLRRuntimeHost = nullptr;
    __try
    {
        HRESULT hr;
        // Получаем среду выполнения
        hr = CLRCreateInstance( CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost );
        if ( FAILED(hr) ) return -1;

        hr = pMetaHost->GetRuntime( L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&pCLRRuntimeInfo );

        BOOL bCLRIsLoadable;
        hr = pCLRRuntimeInfo->IsLoadable( &bCLRIsLoadable );
        if ( FAILED( hr ) ) return -1;
        if ( ! bCLRIsLoadable ) return -1;
        
        hr = pCLRRuntimeInfo->GetInterface( CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pCLRRuntimeHost );
        if ( FAILED(hr) ) return -1; 
        
        // Запускаем CLR
        hr = pCLRRuntimeHost->Start();
        if ( FAILED(hr) ) return -1; 

        // Загружаем сборку и вызываем в ней метод
        DWORD nRet;
        hr = pCLRRuntimeHost->ExecuteInDefaultAppDomain( L"TestLib.dll", L"TestLib.Foo", L"Bar", L"", &nRet );
        if ( FAILED(hr) ) return -1; 
    }
    __finally
    {
        // Освобождаем ресурсы
        if ( pCLRRuntimeHost ) { pCLRRuntimeHost->Release(); pCLRRuntimeHost = nullptr; }
        if ( pCLRRuntimeInfo ) { pCLRRuntimeInfo->Release(); pCLRRuntimeInfo = nullptr; }
        if ( pMetaHost ) { pMetaHost->Release(); pMetaHost = nullptr; }
    }
    return 0;
}


Литература
  • Steven Pratschner, Customizing the Microsoft .NET Framework Common Language Runtime
  • Jeffrey Richeter, CLR Via C#
  • CLR Inside out: CLR Hosting APIs, MSDN Magazine, August 2006
  • CLR Inside out: In-Process Side-by-Side, MSDN Magazine, December 2009

Для всех источников, кроме первого, есть переводы на русский язык.