Egyéni .NET-gazdagép írása a .NET-futtatókörnyezet natív kódból való vezérléséhez
Az összes felügyelt kódhoz hasonlóan a .NET-alkalmazásokat is egy gazdagép hajtja végre. A gazdagép feladata a futtatókörnyezet indítása (beleértve az olyan összetevőket is, mint a JIT és a szemétgyűjtő), valamint a felügyelt belépési pontok meghívása.
A .NET-futtatókörnyezet üzemeltetése egy speciális forgatókönyv, és a legtöbb esetben a .NET-fejlesztőknek nem kell aggódniuk a üzemeltetés miatt, mert a .NET-buildfolyamatok alapértelmezett gazdagépet biztosítanak a .NET-alkalmazások futtatásához. Bizonyos speciális körülmények között azonban hasznos lehet explicit módon üzemeltetni a .NET-futtatókörnyezetet, akár a felügyelt kód natív folyamatbeli meghívásának eszközeként, akár azért, hogy jobban szabályozhassa a futtatókörnyezet működését.
Ez a cikk áttekintést nyújt a .NET-futtatókörnyezet natív kódból való elindításához és a felügyelt kód végrehajtásához szükséges lépésekről.
Előfeltételek
Mivel a gazdagépek natív alkalmazások, ez az oktatóanyag egy C++ alkalmazás létrehozását ismerteti a .NET üzemeltetéséhez. Szüksége lesz egy C++ fejlesztési környezetre (például a Visual Studio által biztosítottra).
A gazdagép teszteléséhez .NET-összetevőt is létre kell készítenie, ezért telepítenie kell a .NET SDK-t. Tartalmazza a csatoláshoz szükséges fejléceket és kódtárakat. Például a .NET 8 SDK-val rendelkező Windows rendszerben a fájlok a következő helyen C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native
találhatók: .
API-k üzemeltetése
A .NET-futtatókörnyezet .NET Core 3.0-s és újabb verziókban való üzemeltetése a nethost
hostfxr
kódtárak API-ival történik. Ezek a belépési pontok kezelik a futtatókörnyezet inicializáláshoz való megtalálásának és beállításának összetettségét, és lehetővé teszik a felügyelt alkalmazás elindítását és a statikus felügyelt metódusba való hívást.
A .NET Core 3.0 előtt az egyetlen lehetőség a futtatókörnyezet üzemeltetésére az coreclrhost.h
API-val volt. Ez a üzemeltetési API már elavult, és nem használható .NET Core 3.0-s és újabb futtatókörnyezetek üzemeltetéséhez.
Gazdagép létrehozása a következővel nethost.h
: hostfxr.h
Az alábbi oktatóanyagban ismertetett lépéseket bemutató minta gazdagép a dotnet/samples GitHub-adattárban érhető el. A mintában szereplő megjegyzések egyértelműen társítják az oktatóanyag számozott lépéseit a mintában végzett műveletek helyszínével. A letöltési utasításokért tekintse meg a példákat és az oktatóanyagokat.
Ne feledje, hogy a minta gazdagépet tanulási célokra szánták, ezért a hibaellenőrzésre szolgál, és a hatékonyság feletti olvashatóság hangsúlyozására szolgál.
Az alábbi lépések bemutatja, hogyan indíthatja el a .NET-futtatókörnyezetet natív alkalmazásban a nethost
kódtárak és hostfxr
kódtárak használatával, és hogyan hívhat meg egy felügyelt statikus metódust. A minta a nethost
.NET SDK-val telepített fejléceket és kódtárakat, coreclr_delegates.h
valamint hostfxr.h
fejléceket használja.
1. lépés – Az exportált üzemeltetési függvények betöltése hostfxr
és lekérése
A nethost
kódtár biztosítja a get_hostfxr_path
függvényt a tár helyének hostfxr
kiválasztásához. A hostfxr
kódtár függvényeket tesz elérhetővé a .NET-futtatókörnyezet üzemeltetéséhez. A függvények teljes listája megtalálható a natív üzemeltetési tervdokumentumban.hostfxr.h
A minta és az oktatóanyag a következőket használja:
hostfxr_initialize_for_runtime_config
: Inicializál egy gazdagépkörnyezetet, és előkészíti a .NET-futtatókörnyezet inicializálását a megadott futtatókörnyezet-konfigurációval.hostfxr_get_runtime_delegate
: Meghatalmazottat kér le a futtatókörnyezeti funkciókhoz.hostfxr_close
: Bezár egy gazdagépkörnyezetet.
A hostfxr
kódtár a kódtár API-jának nethost
használatával get_hostfxr_path
található. Ezután betöltődik, és a rendszer lekéri az exportálását.
// 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);
}
A minta a következőket használja:
#include <nethost.h>
#include <coreclr_delegates.h>
#include <hostfxr.h>
Ezek a fájlok a következő helyeken találhatók:
- 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
Vagy ha telepítette a .NET 8 SDK-t Windows rendszeren:
C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x64\8.0.4\runtimes\win-x64\native
2. lépés – A .NET-futtatókörnyezet inicializálása és elindítása
Az hostfxr_initialize_for_runtime_config
and hostfxr_get_runtime_delegate
functions inicializálja és elindítja a .NET-futtatókörnyezetet a betöltendő felügyelt összetevő futtatókörnyezet-konfigurációjának használatával. A hostfxr_get_runtime_delegate
függvény egy futtatókörnyezeti delegált lekérésére szolgál, amely lehetővé teszi egy felügyelt szerelvény betöltését, és egy függvénymutatót kap egy statikus metódushoz az adott szerelvényben.
// 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. lépés – Felügyelt szerelvény betöltése és függvénymutató lekérése felügyelt metódushoz
A futtatókörnyezeti delegált a felügyelt szerelvény betöltésére és egy függvénymutató felügyelt metódushoz való lekérésére van meghívva. A meghatalmazottnak bemenetként meg kell adnia a szerelvény elérési útját, a típusnevet és a metódusnevet, és visszaad egy függvénymutatót, amely a felügyelt metódus meghívására használható.
// 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
A futtatókörnyezeti delegált meghívásakor a minta egy alapértelmezett aláírást használ a felügyelt metódushoz:
public delegate int ComponentEntryPoint(IntPtr args, int sizeBytes);
A futtatókörnyezeti delegált meghívásakor más aláírás használható a meghatalmazott típusnevének megadásával.
4. lépés – Felügyelt kód futtatása!
A natív gazdagép mostantól meghívhatja a felügyelt metódust, és átadhatja a kívánt paramétereket.
lib_args args
{
STR("from host!"),
i
};
hello(&args, sizeof(args));