Megosztás a következőn keresztül:


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.

API-k üzemeltetése

A .NET-futtatókörnyezet .NET Core 3.0-s és újabb verziókban való üzemeltetése a nethosthostfxr 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écet és tárat, valamint a coreclr_delegates.hdotnet/runtime-adattárból származó fájlok másolatait hostfxr.h 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:

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));