-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
[API Proposal]: coreclr retrieving an existing host handle #56896
Comments
Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov Issue DetailsBackground and motivationCurrently, Situation: one is writing a plugin which is to be loaded by a native host. The native host requires the plugin to have a native entry point and is completely out of his control. At the same time, there are also other plugins which are also completely out of his control. Request: A way for native users other than the first caller of API Proposal//
// Retrieving an already created host handle, provided that there is any.
//
// Parameters:
// [out] hostHandle - Handle of an already created host
//
// Returns:
// HRESULT indicating status of the operation. S_OK if there is a suitable handle.
//
extern int coreclr_get_active_host(void** hostHandle); API UsageHRESULT hr;
void* hostHandle;
hr = coreclr_get_active_host(&hostHandle);
if (SUCCEEDED(hr)) {
// coreclr_initialize is already called, try to load my code into this host.
} else {
// try to do host initialization myself.
} RisksIt is not always possible to load an assembly to a host created by others, the .NET version may differ, some properties may cause conflict, etc.
|
This should be supported since .NET Core 3.0. I should start by saying that native hosting should not call There's a helper library Once you have the There many more details on this in the same design doc. I don't think we have a sample which would do this, but the idea is that the hosting code should not really see a big difference - the methods to call are exactly the same. So the https://github.com/dotnet/samples/tree/main/core/hosting/HostWithHostFxr sample is basically the exact same code one would use. The design doc also discusses what functionality exists to compare the requirements of the new plugin and the existing runtime in the process (framework version resolution and so on). |
Yes, I'm aware of the new |
I agree that the messaging around this is problematic (we should make it much more clear that directly calling That said, I don't think we should be providing two APIs to solve the same problem. The In addition to that - it would basically mean that every component should be using the
We can't prevent people doing weird things - you could call I think we should fix the docs and basically remove the direct |
I totally agree that it is not a good idea to do weird things. But the real problem is, it is not the one who have done weird things that will actually suffer from the consequence, in fact their components just work as expected. Hell, they cannot even be blamed of doing weird things, as long as the As a cooperative plugin author, it is perfectly acceptable to prefer the "right thing to do" at first (e.g. use I am aware that the proposed
It will be good if there is such a last resort before completely giving up and showing an ugly error dialog blaming others doing weird things (the end user will not care, of course) and hoping that the end user will understand this complex technical situation and find the right one to blame (they will not, of course). Appendix.
As you can see, the proposed API will not drive everyone to use |
If all components behaved the way you describe it, that would be great. I just don't have the confidence that it's going to be that case. But I guess that's also an argument for adding the new API (since as you already mentioned there are exiting bad-behaving components). The reason I'm a bit hesitant still is that basically we would be adding an API which is unsupported from the very beginning. It's just really weird. The API would have to be "unsupported" because there are so many caveats around using it - there would be basically no meaningful way to use it and be confident that the scenario works - it would all be "hope it works". The other reason I'm hesitant is that if it doesn't work, the failure modes will be all over the place and super hard to diagnose. I do understand the need for it if you run into the case you describe though. Just to set expectations - even if we do decide to do it, it would not make .NET 6 (it's way too late for that). I wanted to ask some additional questions since the 7 steps you describe above seem a bit weird to me: 1 - I assume you do all these tricks to have effectively self-contained component, right? 2 - I haven't had a chance to look into #56968 yet. My guess is that this is because the host has a bit of a weird behavior (long history) that if the app has 4 - Using the same directory for self-contained and framework-dependent app leads to trouble - the host is just not made to work in that case. (which is probably the reason for the issues in step 2) 6 - Somebody loaded If you really need to have the component self-contained (which is problematic and causes lot of trouble as you're well aware of), there might be a better way to do this:
This will give you benefits:
And some downsides:
We're discussing a very similar approach to allow multiple executables to share effectively self-contained runtime here: #53834 It still doesn't solve the problem of cooperation with bad-behaved components though. |
I'm well aware that dealing with uncooperative third party components is nevertheless a mess and cannot be done in a clean and reliable way. But I do think the self-contained component is a justified use case (As I described in 1). The ultimate solution can be supporting side-by-side hosting, but I do not think this will be implemented anytime soon. So in the meanwhile we still need to deal with various limitations and caveats. I understand you hesitation of adding a public API whose use cases are questionable at its very nature, but we may think this problem the other way:
|
We don't have docs around this since there's no direct support for it in the SDK.
We already consider them deprecated to a large degree - but it's not correctly reflected in the docs unfortunately. We definitely encourage everyone to NOT use it. Just a note on the word "deprecated" here - the APIs will remain and probably keep working as-is for a while (we kind of have to do that for backward compat reasons). While I agree that with open source software it's much easier to "hack" around into the "unsupported" things I think there's still a big difference:
So typically such solution might work well in one version, but can easily break in future versions. On the topic of self-contained components (plugins) - while I understand the problem with distributing shared framework install, using self-contained deployment model is also not ideal. At least until there's some kind of support for hosting multiple versions SxS in one process. This doesn't have a good solution right now, but what we thought might be acceptable is:
If one component breaks this (by either using different roll forward setting, or being self-contained), then it is very easy to break things by just loading components in different order. The self-contained is somewhat worse in that it could in theory ship some weird/unsupported version of the runtime (or for example delete some parts of it to save space and so on) - any component loaded after it would have to be able to run on that - which is unreasonable to support correctly. |
I'm pretty aware of what you said about compatibility concerns, and I agree that a cooperative component should try loading the framework runtime whenever possible. The core idea is, people need to make things work, the right/expected way or not. Whenever there is something not work, there will be a pressure. Even a nice and cooperative author is willing to stick to the best practice at first, he will start doing hacky/unexpected (but still strictly speaking legal) things if "the best practice" cannot work. And, well, if it still does not work, then even the most nice and cooperative author will become mad, and either start doing all kinds of "bad things", or he eventually comes to a conclusion that the whole framework does not deserve investment and then throws it out of the window and switches to something "all the way dirty and out of date but can make things work". |
The CoreCLR direct hosting sample and guide has been removed in dotnet/docs#25818 and dotnet/samples#4786. |
Background and motivation
Currently,
coreclr_initialize
is not supported to be called more than once. On the other hand, there is not any method to retrieve an already initialized host, either.As a result, if one ever want to execute any .NET Core code from native, he must be the first one to contact coreclr, or he is completely out of luck.
Situation: one is writing a plugin which is to be loaded by a native host. The native host requires the plugin to have a native entry point and is completely out of his control. At the same time, there are also other plugins which are also completely out of his control.
Then he want to write part of his code in C#, thus necessitate the hosting of .NET Core. However, he is not the first one to be clever enough to use .NET Core, and there is already someone loading coreclr into the process.
It is not hard to retrieve an already loaded coreclr module (by GetModuleHandleW, for example), but even if one has access to the coreclr module, he cannot run any managed code if
coreclr_initialize
is already called by others and the returned host handle is not shared to him.He is okay with the limitations like only one version of coreclr can exist, the host can only be initialized once, etc. He is also willing to accept some caveats like the host initialized by others might not have properties set to what exactly he wants. All he want is just having a chance to even try to load a managed assembly and execute some code.
But he is still completely out of luck in this case. Any coreclr method to load managed code requires a host handle to start with, but he has no way to get such a handle unless he is the lucky one making the first ever call to
coreclr_initialize
among the whole process.Request: A way for native users other than the first caller of
coreclr_initialize
to have a chance to try to load and execute any managed code, provided that they are already aware of any limitation and caveats, like only one coreclr version can exist and the host properties cannot be changed.API Proposal
API Usage
Risks
It is not always possible to load an assembly to a host created by others, the .NET version may differ, some properties may cause conflict, etc.
Nonetheless, without such an API it will be plain impossible to ever have a try.
The text was updated successfully, but these errors were encountered: