Napsání vlastního hostitele .NET pro řízení modulu runtime .NET z nativního kódu
Stejně jako všechny spravované kódy jsou aplikace .NET spouštěné hostitelem. Hostitel zodpovídá za spuštění modulu runtime (včetně komponent, jako je JIT a uvolňování paměti) a vyvolání spravovaných vstupních bodů.
Hostování modulu runtime .NET je pokročilý scénář a vývojáři .NET se ve většině případů nemusí starat o hostování, protože procesy sestavení .NET poskytují výchozího hostitele pro spouštění aplikací .NET. V některých specializovaných případech ale může být užitečné explicitně hostovat modul runtime .NET, a to buď jako prostředek vyvolání spravovaného kódu v nativním procesu, nebo za účelem získání větší kontroly nad fungováním modulu runtime.
Tento článek poskytuje přehled kroků potřebných ke spuštění modulu runtime .NET z nativního kódu a spuštění spravovaného kódu v něm.
Požadavky
Vzhledem k tomu, že hostitelé jsou nativní aplikace, tento kurz se zabývá vytvořením aplikace C++, která bude hostitelem .NET. Budete potřebovat vývojové prostředí C++ (například prostředí poskytované sadou Visual Studio).
Budete také muset sestavit komponentu .NET pro otestování hostitele, takže byste měli nainstalovat sadu .NET SDK. Obsahuje potřebné hlavičky a knihovny pro propojení. Například ve Windows se sadou .NET 8 SDK najdete soubory v C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native
souboru .
Hostování rozhraní API
Hostování modulu runtime .NET v .NET Core 3.0 a novějších se provádí s rozhraními nethost
API a hostfxr
rozhraními API knihoven. Tyto vstupní body zpracovávají složitost hledání a nastavení modulu runtime pro inicializaci a umožňují spuštění spravované aplikace i volání do statické spravované metody.
Před .NET Core 3.0 byla jediná možnost hostování modulu runtime prostřednictvím coreclrhost.h
rozhraní API. Toto rozhraní API pro hostování je zastaralé a nemělo by se používat k hostování .NET Core 3.0 a novějších modulů runtime.
Vytvoření hostitele pomocí nethost.h
a hostfxr.h
Ukázkový hostitel demonstrující kroky popsané v následujícím kurzu je k dispozici v úložišti dotnet/samples na GitHubu. Komentáře v ukázce jasně přidružují očíslované kroky z tohoto kurzu k tomu, kde se v ukázce provádějí. Pokyny ke stažení najdete v tématu Ukázky a kurzy.
Mějte na paměti, že ukázkový hostitel je určený k použití pro účely učení, takže je light na kontrole chyb a je navržený tak, aby zdůraznil čitelnost nad efektivitou.
Následující kroky podrobně popisuje, jak pomocí nethost
knihoven hostfxr
spustit modul runtime .NET v nativní aplikaci a volat spravovanou statickou metodu. Ukázka používá nethost
hlavičky a knihovny a hostfxr.h
coreclr_delegates.h
hlavičky nainstalované se sadou .NET SDK.
Krok 1 : Načtení hostfxr
a export exportování hostitelských funkcí
Knihovna nethost
poskytuje get_hostfxr_path
funkci pro vyhledání hostfxr
knihovny. Knihovna hostfxr
zveřejňuje funkce pro hostování modulu runtime .NET. Úplný seznam funkcí najdete v hostfxr.h
nativním dokumentu návrhu hostování. Ukázka a tento kurz používají následující:
hostfxr_initialize_for_runtime_config
: Inicializuje kontext hostitele a připraví na inicializaci modulu runtime .NET pomocí zadané konfigurace modulu runtime.hostfxr_get_runtime_delegate
: Získá delegáta pro funkce modulu runtime.hostfxr_close
: Zavře kontext hostitele.
Knihovna hostfxr
se nachází pomocí get_hostfxr_path
rozhraní API z nethost
knihovny. Pak se načte a jeho exporty se načtou.
// 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);
}
Ukázka používá následující prvky:
#include <nethost.h>
#include <coreclr_delegates.h>
#include <hostfxr.h>
Tyto soubory najdete v následujících umístěních:
- 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
Nebo pokud jste nainstalovali sadu .NET 8 SDK ve Windows:
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native
Krok 2 – Inicializace a spuštění modulu runtime .NET
hostfxr_get_runtime_delegate
Funkce hostfxr_initialize_for_runtime_config
inicializují a spustí modul runtime .NET pomocí konfigurace modulu runtime pro spravovanou komponentu, která se načte. Funkce hostfxr_get_runtime_delegate
slouží k získání delegáta modulu runtime, který umožňuje načtení spravovaného sestavení a získání ukazatele funkce na statickou metodu v daném sestavení.
// 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;
}
Krok 3 – Načtení spravovaného sestavení a získání ukazatele funkce na spravovanou metodu
Delegát modulu runtime se volá k načtení spravovaného sestavení a získání ukazatele funkce na spravovanou metodu. Delegát vyžaduje cestu sestavení, název typu a název metody jako vstupy a vrátí ukazatel funkce, který lze použít k vyvolání spravované metody.
// 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
Předáním názvu typu delegáta při volání delegáta modulu runtime použije ukázka výchozí podpis pro spravovanou metodu:
public delegate int ComponentEntryPoint(IntPtr args, int sizeBytes);
Jiný podpis lze použít zadáním názvu typu delegáta při volání delegáta modulu runtime.
Krok 4 : Spuštění spravovaného kódu
Nativní hostitel teď může volat spravovanou metodu a předat jí požadované parametry.
lib_args args
{
STR("from host!"),
i
};
hello(&args, sizeof(args));