Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Как и весь управляемый код, приложения .NET выполняются узлом. Хост отвечает за запуск среды выполнения (включая такие компоненты, как JIT и сборщик мусора) и вызов управляемых точек входа.
Хостинг среды выполнения .NET — это расширенный сценарий, и в большинстве случаев разработчикам .NET не нужно беспокоиться о хостинге, так как процессы сборки .NET предоставляют хост по умолчанию для запуска приложений .NET. Однако в некоторых специализированных обстоятельствах может быть полезно явно разместить среду выполнения .NET как средство вызова управляемого кода в собственном процессе, так и для получения большего контроля над тем, как работает среда выполнения.
В этой статье приводятся общие сведения о шагах, необходимых для запуска среды выполнения .NET из машинного кода и выполнения управляемого кода в нем.
Предпосылки
Поскольку хосты являются независимыми приложениями, в этом руководстве рассматривается создание приложения C++ для хостинга .NET. Вам потребуется среда разработки C++ (например, предоставляемая Visual Studio).
Вам также потребуется создать компонент .NET для тестирования узла, поэтому необходимо установить пакет SDK для .NET. Он включает необходимые заголовки и библиотеки для линковки. Например, в Windows с SDK .NET 8 файлы можно найти в C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native
.
Размещение API-интерфейсов
Размещение среды выполнения .NET в .NET Core 3.0 и более поздних версиях осуществляется с помощью API библиотек nethost
и hostfxr
. Эти точки входа обрабатывают сложность поиска и настройки среды выполнения для инициализации и позволяют запускать управляемое приложение и вызывать статический управляемый метод.
До .NET Core 3.0 единственный вариант размещения среды выполнения был через coreclrhost.h
API. Этот API размещения устарел и не должен использоваться для размещения сред выполнения .NET Core 3.0 и более поздних версий.
Создание узла с помощью nethost.h
и hostfxr.h
Пример узла, демонстрирующий шаги, описанные в приведенном ниже руководстве, доступен в репозитории dotnet/samples GitHub. Примечания в примере четко связывают нумерованные шаги из этого руководства с тем, где они выполняются в примере. Инструкции по скачиванию смотрите в разделах Образцы и руководства.
Имейте в виду, что образцовый хост предназначен для обучения, поэтому он легковесен в проверке ошибок и подчеркивающий удобочитаемость за счет эффективности.
Ниже описано, как использовать nethost
и hostfxr
библиотеки для запуска среды выполнения .NET в собственном приложении и вызова управляемого статического метода. В примере используются nethost
заголовки и библиотеки, а также coreclr_delegates.h
и hostfxr.h
заголовки, установленные с пакетом SDK для .NET.
Шаг 1. Загрузите hostfxr
и получите экспортированные функции хостинга
Библиотека nethost
предоставляет функцию get_hostfxr_path
для поиска библиотеки hostfxr
. Библиотека hostfxr
предоставляет функции для размещения среды выполнения .NET. Полный список функций можно найти в hostfxr.h
и в документе проектирования нативного размещения. В примере и этом руководстве используется следующее:
-
hostfxr_initialize_for_runtime_config
: инициализирует контекст узла и готовится к инициализации среды выполнения .NET с помощью указанной конфигурации среды выполнения. -
hostfxr_get_runtime_delegate
: получает делегат для функциональности среды выполнения. -
hostfxr_close
: закрывает контекст узла.
Библиотека hostfxr
найдена с помощью get_hostfxr_path
API из nethost
библиотеки. Затем его загружают и извлекают экспорт.
// Using the nethost library, discover the location of hostfxr and get exports
bool load_hostfxr()
{
// Pre-allocate a large buffer for the path to hostfxr
char_t buffer[MAX_PATH];
size_t buffer_size = sizeof(buffer) / sizeof(char_t);
int rc = get_hostfxr_path(buffer, &buffer_size, nullptr);
if (rc != 0)
return false;
// Load hostfxr and get desired exports
void *lib = load_library(buffer);
init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(lib, "hostfxr_initialize_for_runtime_config");
get_delegate_fptr = (hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
return (init_fptr && get_delegate_fptr && close_fptr);
}
В примере используются следующие компоненты:
#include <nethost.h>
#include <coreclr_delegates.h>
#include <hostfxr.h>
Эти файлы можно найти в следующих расположениях:
- https://github.com/dotnet/runtime/blob/main/src/native/corehost/nethost/nethost.h
- https://github.com/dotnet/runtime/blob/main/src/native/corehost/coreclr_delegates.h
- https://github.com/dotnet/runtime/blob/main/src/native/corehost/hostfxr.h
Или, если вы установили пакет SDK для .NET 8 в Windows:
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native
Шаг 2. Инициализация и запуск среды выполнения .NET
Функции hostfxr_initialize_for_runtime_config
и hostfxr_get_runtime_delegate
инициализируют и запускают среду выполнения .NET с использованием конфигурации среды выполнения для управляемого компонента, который будет загружен. Эта hostfxr_get_runtime_delegate
функция используется для получения делегата среды выполнения, который позволяет загружать управляемую сборку и получать указатель функции на статический метод в этой сборке.
// Load and initialize .NET Core and get desired function pointer for scenario
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *config_path)
{
// Load .NET Core
void *load_assembly_and_get_function_pointer = nullptr;
hostfxr_handle cxt = nullptr;
int rc = init_fptr(config_path, nullptr, &cxt);
if (rc != 0 || cxt == nullptr)
{
std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
close_fptr(cxt);
return nullptr;
}
// Get the load assembly function pointer
rc = get_delegate_fptr(
cxt,
hdt_load_assembly_and_get_function_pointer,
&load_assembly_and_get_function_pointer);
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
close_fptr(cxt);
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
Шаг 3. Загрузка управляемой сборки и получение указателя на функцию для управляемого метода
Делегат среды выполнения вызывается для загрузки управляемой сборки и получения указателя функции на управляемый метод. Делегату требуется путь сборки, имя типа и имя метода в качестве входных данных и возвращает указатель функции, который можно использовать для вызова управляемого метода.
// Function pointer to managed delegate
component_entry_point_fn hello = nullptr;
int rc = load_assembly_and_get_function_pointer(
dotnetlib_path.c_str(),
dotnet_type,
dotnet_type_method,
nullptr /*delegate_type_name*/,
nullptr,
(void**)&hello);
Передавая nullptr
как имя типа делегата при вызове делегата среды выполнения, в примере используется сигнатура по умолчанию для управляемого метода.
public delegate int ComponentEntryPoint(IntPtr args, int sizeBytes);
Другую сигнатуру можно использовать, указав имя типа делегата при вызове делегата среды выполнения.
Шаг 4. Запуск управляемого кода!
Теперь нативный хост может вызвать управляемый метод и передать ему необходимые параметры.
lib_args args
{
STR("from host!"),
i
};
hello(&args, sizeof(args));
Ограничения
В одном процессе можно загрузить только одну среду выполнения.
hostfxr_initialize_for_runtime_config
Если API вызывается при загрузке среды выполнения, он проверяет, совместима ли существующая среда выполнения с указанными параметрами инициализации. В случае совместимости существующая среда выполнения будет использоваться и, если она не совместима, API вернет сбой.