Skip to content

Trying to load a self-contained dll, with an own runtime-host #35329

Closed
@unglaublicherdude

Description

@unglaublicherdude

Hi,

I am trying to get a project running, where I don't want to rely on a installed framework on the users system. So I tried the following setup:

My hosting-code:

#include <iostream>

#include "pch.h"
#include <windows.h>
#include <assert.h>
#include <string>
#include <ShlObj.h>
#include "nethost.h"
#include "hostfxr.h"
#include "coreclr_delegates.h"

using std::wstring;

#define STR(s) L ## s
#define CH(c) L ## c
#define DIR_SEPARATOR L'\\'

namespace
{
    void load_lib(std::string lib_name)
    {
        auto load_lib_result = LoadLibraryA(lib_name.c_str());
        if (load_lib_result != NULL)
        {
            std::string debug_message = "success loading -> " + lib_name;
            printf(debug_message.c_str());
            FreeLibrary(load_lib_result);
        }
        else
        {
            int errorCode = GetLastError();
            std::string debug_message = "loading " + lib_name + " " + std::to_string(errorCode) + " failed";
            printf(debug_message.c_str());
        }
    }

    
    hostfxr_initialize_for_runtime_config_fn init_fptr;
    hostfxr_get_runtime_delegate_fn get_delegate_fptr;
    hostfxr_close_fn close_fptr;

    void* load_library(const char_t*);
    void* get_export(void*, const char*);

    void* load_library(const char_t* path)
    {
        HMODULE h = ::LoadLibraryW(path);
        assert(h != nullptr);
        return (void*)h;
    }
    void* get_export(void* h, const char* name)
    {
        void* f = ::GetProcAddress((HMODULE)h, name);
        assert(f != nullptr);
        return f;
    }
}

load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* config_path)
{
    void* load_assembly_and_get_function_pointer = nullptr;
    hostfxr_handle cxt = nullptr;

    hostfxr_initialize_parameters parameters{
        sizeof(hostfxr_initialize_parameters),
        L".\\",
        L".\\"
    };
	
    int rc = init_fptr(config_path, &parameters, &cxt);
    if (rc > 1 || cxt == nullptr)
    {
        std::string error_message = "Init failed with rc: " + std::to_string(rc);
        printf(error_message.c_str());
        close_fptr(cxt);
        return nullptr;
    }

    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::string error_message = "Get delegate failed with rc: " + std::to_string(rc);
        printf(error_message.c_str());
    }

    close_fptr(cxt);
    return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}

bool load_hostfxr()
{
    char_t buffer[MAX_PATH] = L".\\hostfxr.dll";

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

namespace
{
    bool isInitialized = false;
    component_entry_point_fn moduleStart = nullptr;
    component_entry_point_fn moduleStop = nullptr;

    int init()
    {
        printf(">> DnsCLoudClientEntry::init\n");

        if (!isInitialized)
        {
            if (!load_hostfxr())
            {
                printf("<< DnsCLoudClientEntry::init exit with failure in load_hostfxr()\n");
                assert(false && "Failure: load_hostfxr()");
                return EXIT_FAILURE;
            }

            wstring workingDirectory = L".\\";

            const wstring config_path = workingDirectory + STR("ElTesto.runtimeconfig.json");
            load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
            load_assembly_and_get_function_pointer = get_dotnet_load_assembly(config_path.c_str());
            assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()");

            const wstring dotnetlib_path = workingDirectory + STR("ElTesto.dll");
            const char_t* dotnet_type = STR("ElTesto.ElTesto, ElTesto");

            const char_t* dotnet_type_method = STR("FooBar");
            int rc = load_assembly_and_get_function_pointer(
                dotnetlib_path.c_str(),
                dotnet_type,
                dotnet_type_method,
                nullptr,
                nullptr,
                (void**)&moduleStart);
            assert(rc == 0 && moduleStart != nullptr && "Failure: load_assembly_and_get_function_pointer() ModuleStart");

            isInitialized = true;
        }
        printf("<< DnsCLoudClientEntry::init\n");
        return 0;
    }
}

int main()
{
    init();
    struct lib_args
    {
        const char_t* message;
        int number;
    };
    lib_args args{ STR(""), 1 };
    moduleStart(nullptr, 0);
}

The C# library has no dependencies besides the .Net Core runtime. I build it with this command line-command:
dotnet publish -r win-x64 -c release -f netcoreapp3.1 --self-contained

Now two questions:

  1. The publish command generates a runtimeconfig like this
{
  "runtimeOptions": {
    "tfm": "netcoreapp3.1",
    "includedFramework": [
      {
        "name": "Microsoft.NETCore.App",
        "version": "3.1.3"
      }
    ]
  }
}

Running the wrapper with that configuration I get an Error:
Initialization for self-contained components is not supported

Changing the runtimeconfig to this makes it run:

{
  "runtimeOptions": {
    "tfm": "netcoreapp3.1",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "3.1.3"
    }
  }
}

But I still have the problem, the it isn't fully self-contained, because while calling hostfxr_initialize_for_runtime_config it will always try getting some files system folders. Do i just missunderstand self-contained?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions