Dela via


Skriv en anpassad .NET-värd för att kontrollera .NET-körmiljön från din infödda kod

Precis som all hanterad kod körs .NET-program av en värd. Värden ansvarar för att starta körmiljön (inklusive komponenter som JIT och skräpinsamlaren) och anropa hanterade programsstartpunkter.

Att vara värd för .NET-körningen är ett avancerat scenario och i de flesta fall behöver .NET-utvecklare inte bekymra sig om värdtjänster eftersom .NET-byggprocesser tillhandahåller en standardvärd för att köra .NET-program. Under vissa särskilda omständigheter kan det dock vara användbart att uttryckligen vara värd för .NET-körningen, antingen som ett sätt att anropa hanterad kod i en intern process eller för att få mer kontroll över hur körningen fungerar.

Den här artikeln ger en översikt över de steg som krävs för att starta .NET-körningen från intern kod och köra hanterad kod i den.

Förutsättningar

Eftersom värdarna är inbyggda applikationer handlar denna handledning om att skapa ett C++-program som är värd för .NET. Du behöver en C++-utvecklingsmiljö (till exempel den som tillhandahålls av Visual Studio).

Du måste också skapa en .NET-komponent för att testa värden med, så du bör installera den senaste versionen av .NET SDK. Den innehåller de rubriker och bibliotek som behövs för att länka till.

Värd-API:er

Värdskap av .NET-runtime görs med biblioteken nethost och hostfxr API:erna. Dessa startpunkter hanterar komplexiteten i att hitta och konfigurera körningen för initiering och gör det möjligt att både starta ett hanterat program och anropa till en statisk hanterad metod.

Skapa en värd med hjälp av nethost.h och hostfxr.h

En exempelvärd som visar stegen som beskrivs i handledningen nedan finns i GitHub-arkivet dotnet/samples. Kommentarer i exemplet associerar tydligt de numrerade stegen från den här självstudien med var de utförs i exemplet. Instruktioner för nedladdning finns i Exempelfiler och Handledningar.

Tänk på att exempelvärden är tänkt att användas i utbildningssyfte, så det är lätt på felkontroll och utformat för att betona läsbarhet framför effektivitet.

Följande steg beskriver hur du använder biblioteken nethost och hostfxr för att starta .NET-körningen i ett internt program och anropa till en hanterad statisk metod. Exemplet använder biblioteken och nethost- och coreclr_delegates.h-huvuden samt hostfxr.h-huvuden som är installerade med .NET SDK.

Steg 1 – Läsa in hostfxr och hämta exporterade värdfunktioner

nethost-biblioteket tillhandahåller funktionen get_hostfxr_path för att lokalisera hostfxr-biblioteket. Biblioteket hostfxr exponerar funktioner för att värda .NET-körmiljön. Den fullständiga listan över funktioner finns i hostfxr.h och det interna värddesigndokumentet. Exemplet och den här självstudien använder följande:

  • hostfxr_initialize_for_runtime_config: Initierar en värdkontext och förbereder för initiering av .NET-körningen med den angivna körningskonfigurationen.
  • hostfxr_get_runtime_delegate: Hämtar en delegering för körningsfunktioner.
  • hostfxr_close: Stänger en värdkontext.

Biblioteket hostfxr hittas med hjälp av get_hostfxr_path API:et från nethost biblioteket. Den tas sedan in och dess exporter hämtas.

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

Exemplet använder följande inkluderingar:

#include <nethost.h>
#include <coreclr_delegates.h>
#include <hostfxr.h>

Dessa filer finns på följande platser:

Steg 2 – Initiera och starta .NET-körningen

Funktionerna hostfxr_initialize_for_runtime_config och hostfxr_get_runtime_delegate initierar och startar .NET-körningen med hjälp av körningskonfigurationen för den hanterade komponent som ska läsas in. Funktionen hostfxr_get_runtime_delegate används för att hämta ett körningsdelegat som gör det möjligt att läsa in en hanterad sammansättning och hämta en funktionspekare till en statisk metod i sammansättningen.

// 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;
}

Steg 3 – Läsa in hanterad sammansättning och hämta funktionspekaren till en hanterad metod

Runtime-ombudet anropas för att läsa in den hanterade sammansättningen och hämta en funktionspekare till en hanterad metod. Delegaten kräver sammansättningssökvägen, typnamnet och metodnamnet som indata och returnerar en funktionspekare som kan användas för att anropa den förvaltade metoden.

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

Genom att skicka nullptr som namn på ombudstyp när du anropar körningsdelegaten använder exemplet en standardsignatur för den hanterade metoden:

public delegate int ComponentEntryPoint(IntPtr args, int sizeBytes);

En annan signatur kan användas genom att ange namnet på ombudstypen när du anropar runtime-ombudet.

Steg 4 – Kör hanterad kod!

Den inhemska värden kan nu anropa den hanterade metoden och skicka de önskade parametrarna.

lib_args args
{
    STR("from host!"),
    i
};

hello(&args, sizeof(args));

Begränsningar

Endast en körmiljö kan laddas in i en enda process. Om hostfxr_initialize_for_runtime_config-API:et anropas när en runtime-miljö redan är inläst, kontrollerar den om den befintliga miljön är kompatibel med de angivna initieringsparametrarna. Om den är kompatibel används den befintliga körningen och om den inte är kompatibel returnerar API:et ett fel.