-
Notifications
You must be signed in to change notification settings - Fork 5k
Trying to load a self-contained dll, with an own runtime-host #35329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
We got it working in a folder structure like this:
The main problem is, that it tries to load the hostpolicy.dll from the following path: |
Tagging subscribers to this area: @vitek-karas, @swaroop-sridhar |
We now have this folder structure:
We basically copied the output from We now we tourned all servicable flags from This makes our setup work without, trying to load anything from any other folder. Question: Is this a doable way? We are curious if there is another way than copying that many files and manually editing the |
Currently we don't support loading "self-contained components" into native hosts. In fact we don't support self-contained components - full stop (anywhere really). That's why you get the error message What I don't understand is why do you need to write your own native host in this case?
|
Thank you, for your answer. We are running in a PPL (Protected Process Light) context. Getting loaded by an already registered process (this is also the reason, why we need to have in our hand, which DLLs get loaded while running our component). So what we do is: We have a process, that is registered as PPL. This process does load a native DLL (in our case, the custom host), that then does run our .Net Component. The problem with the PPL context is, that we have to define certificate signatures that get accepted in our PPL context. So we cannot rely on a framework version that is installed on the system, because the certificate can change even in patch-level updates, which would break our component. The setup we actually have is working, by basically mirroring the folder structure of the dotnet installation path and copying all the framework DLLs over and then editing the |
If you only want to run one component (so it's basically an app in that sense) I would strongly suggest you use the There's another possibility which is to install your own copy of the runtime. You can download a zip (instead of an installer) of the runtime, unzip to any location you want. Then run the app either via the |
Thank you again. We will try both of these ways. I think the |
I basically did that now, with setting the DOTNET_MULTILEVEL_LOOKUP. But in the ZIP-Package of the .Net runtime there are DLLs still marked as servicable. So what happens is, that the host will still look in the servicing-path first. 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"C:\\myarbitaryapppath\\",
L"C:\\mycustomruntimepath\\dotnet\\"
};
int rc = init_fptr(config_path, ¶meters, &cxt);
} bool load_hostfxr()
{
// char_t buffer[MAX_PATH] = L".\\hostfxr.dll";
// Pre-allocate a large buffer for the path to hostfxr
char_t buffer[MAX_PATH];
size_t buffer_size = sizeof(buffer) / sizeof(char_t);
get_hostfxr_parameters parameters{
sizeof(hostfxr_initialize_parameters),
nullptr,
L"C:\\mycustomruntimepath\\dotnet"
};
int rc = get_hostfxr_path(buffer, &buffer_size, ¶meters);
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);
} int init()
{
SetEnvironmentVariable(TEXT("DOTNET_MULTILEVEL_LOOKUP"), TEXT("0"));
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"C:\\myarbitaryapppath\\";
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()"); This is only prevented by also editing the According to this Readme I think this will always happen. Which should be ok because we have anyways have to deploy the whole framework by ourselfs, so we can just edit the deps.json. For the hostpolicy.dll I also confirmed that behaviour by this code. The servicing path is the first that is tried by the resolver and can basically just be avoided by removing the version in the deps.json or - but that would be even more hacky overwriting the |
Right - the servicing (also called hammer servicing) is intentionally not easy to disable. It's for cases were there is a serious security problem which we would need to fix. Note that it is VERY version specific (it will only kick in if the exact version is available in the servicing path). And AFAIK it has never been used. And I can't imagine us using this and changing the code signing cert in such an update. But if you can't live with that, the only way out really is modifying the Still curious: Why do you need to use initialize_for_runtimeconfig and load_assembly_and_get_function_pointer - not that it won't work, but it seems you're trying to run an app (not load a component into a longer running process). |
I'll try to give you some more information that's understandable. We have a already existing PPL process (this part, we cannot touch), that load and unloads many components (all other components are in C++ written DLLs) with our component beeing the first written in .Net. So this loader service (which is a long running service) would load our custom host (the native DLL) (which also would be long running). By long running I mean as long as the system is running or someone decides to deactivate the component. When I understand your explanation correct, the |
Thanks for the details. If ultimately this scenario can lead to a case where the native host (motherprocess) can activate the managed component, then deactivate it and try to activate some other managed component again, then such solution would not work with self-contained. Currently we don't support unloading the CoreCLR from a process, or allowing two different CoreCLR runtimes in the same process. So supporting two self-contained components won't work. The framework dependent approach is possible in this case as long as all the potentially loaded managed components can agree on one runtime. The first component will load the runtime which will be active in the process for the remainder of its lifetime. In the future other components can use it (but not to load another one). |
There's a related discussion on a very similar topic here: #35465. |
If you're going to load managed code just once into the process then I think #35465 is a solution for this issue. If you need to load multiple components of which some may be self-contained, that is currently not supported - we currently don't have plans to support loading multiple runtimes into the same process. |
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:
Build a c++ wrapper app using this tutorial: Write a custom .NET Core host to control the .NET runtime from your native code
Build a dll in .Net Core 3.1.3 and publish it as self-contained
My hosting-code:
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:
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:
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?The text was updated successfully, but these errors were encountered: