-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Any way to revert to pre-7.0 isolated assembly loading in C++/CLI? #104480
Comments
Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov |
To add a bit more information, what I have is:
So, a "plugin" consists of a folder with the C++/CLI wrapper dll which loads the actual managed plugin code. I can load a single plugin just fine. I can also load the same plugin multiple times from the same folder. Trying to load a second, different plugin, or even the same plugin, but from a different folder, no longer works (it does in .NET 6.0). |
@mikeoliphant There is no current way to revert to the previous C++/CLI behavior. It would be helpful to get a trace - see instructions here. /cc @elinor-fung |
Here is a trace: vsthost.exe_20240705_181811.zip The application activity was:
When the plugin is loaded from a different path, everything goes haywire and the trace keeps getting bigger and bigger. The one I linked above I cut off quickly to keep it from getting too big. From looking at it with perfview (my first time with the tool, so I don't really know what I'm doing) it looks like it is loading the assembly "AudioPlugSharpVst" over and over again. That assembly is my C++/CLI assembly which is the entry point from the native code. |
@mikeoliphant Yep, I see the behavior you are referring to. The solution here is to have a different C++/CLI assembly. We changed this behavior as it more aligned with the majority of C++/CLI use cases coming from .NET Framework. Is it possible for you to adapt to the new behavior? |
This is my github repo: https://github.com/mikeoliphant/AudioPlugSharp The C++/CLI assembly is a framework for loading managed plugins from native code (specifically, VST3 audio plugins). If I'm understanding you correctly, I don't see how it would work for me to have different C++/CLI assemblies per plugin. |
Oh, I see. You provide a single C++/CLI assembly and anyone can drop that next to their plug-in written in .NET and it will get loaded, right? |
Yep, that's exactly it. |
I see. This is going to require feedback from @elinor-fung. My gut tells me this isn't something we are likely to add support for, but I'll defer to the hosting experts. |
If so, that would be very disappointing if I have no possible way to work around it. Can you give me some insight as to why it isn't working? My code should work in both the case that the separate instances of the C++/CLI assembly are loaded independently and isolated, and also in the case that only one copy is loaded and used. |
I actually wouldn't be opposed to adding a switch to allow using an isolated context. We had considered it when switching to the default, but didn't as C++/CLI support was very much targeted at .NET Framework migration. As for why it isn't working - how does the failure to load manifest? When trying to load the plugin from a different path, the runtime should find that |
I think the actual work would be similar to the configuration we added in .NET 8 for loading COM components in the default ALC. I don't think it should be too complicated as there's an existing model for it. If you are interested or have time to take a stab at it, I can point at what I expect needs to happen and we'd welcome contributions. |
It has been hard for me to track down, since when the error occurs it isn't writing anything to my log and I'm not sure how to attach a debugger given the execution environment.
Ah, when it is being loaded a second time from a different location, it keeps the existing assembly and "Assembly.GetExecutingAssembly().Location" will return the original assemblies location? That would definitely cause problems, since that is the only way I know where my other assemblies are located. Although it seems like that might not explain why it has trouble with the same plugin loaded a second time from a different folder - I would think it would just load again from the first folder?
My quickest path to success is probably going to be finding a way to make things work with the new existing behavior, but failing that (or in addition to it) I'm certainly willing to put some time into working on adding a switch to re-enable the previous assembly loading behavior. |
Looking at this more closely, I don't think that it is even getting to my code when trying to load a plugin a second time from a different location. Looking at the trace, I think you can see it succeeding the first two times (both loading from the same place) at around 1:18:18 and 1:18:24. Then I try to load it from a different path (around 1:18:39) and it doesn't get past loading the main C++/CLI assembly ("AudioPlugSharpVst"). My actual plugin loading code is in a referenced assembly - "AudioPlugSharp" - and I don't think it even gets there. It just keeps loading "AudioPluginSharpVst" over and over again. |
Ah, thanks for pointing that out - I was able to look at a repro myself. On load of a C++/CLI assembly, So in the scenario of loading the same C++/CLI assembly from two different paths with the same
I think a switch to allow the isolated ALC behaviour may be the best option here. |
That should solve my problem, and would potentially be useful to others. On the other hand, if, somehow, the behavior could be fixed so that the second assembly load works, but is in a the default ALC (ie: still using the first copy), I think I might be able make things work by getting the plugin location information from the native side rather than the managed side. I also might be able to work around the currently existing behavior by having another C++ dll as my entry point that always loads the AudioPlugSharpVst dll from a fixed location. |
Please let me know if there is anything I can do to help move this forward. |
Here's what I expect needs to happen for a switch:
I don't think our team has the capacity to do this right now, so if you're willing to put some work into adding a switch, we'd welcome it. |
Thanks very much for the detailed information - it is very helpful. |
@elinor-fung Your instructions are clear, and the changes seem fairly straightforward. I've cloned the runtime repository and built it. My struggle right now is with getting an environment set up to test changes. I've got a Core_Root folder created, but I don't know how to make a plugin reference it. I don't think I can use corerun - it seems designed to work with managed assemblies? I can put my modified ijwhost dll in my plugin folder, and that works, but I don't know how to test that alongside code changes to InMemoryAssemblyLoader. Any help would be much appreciated. |
Another issue I'm having is that I can't select .NET 9.0 as the target framework for my C++/CLI project. I'm running latest preview version of Visual Studio, and have the 9.0.100-preview.6.24328.19 SDK installed. |
I still haven't figured out how to test my plugin code against a dev version of the runtime, but I think I've successfully made the necessary changes to the runtime. You can see what I've done on this branch: https://github.com/mikeoliphant/runtime/tree/ijwhost_isolated_switch There are three commits - one for the InMemoryAssemblyLoader change, one for the ijwhost changes and one for the test. |
Ah, yeah, I generally use the
Copying over the modified |
Thanks - that worked. I've now verified that my plugin code works with my runtime changes. As expected, it works when I add "System.Runtime.InteropServices.IJWHost.LoadComponentInIsolatedContext": true". If that config flag is "false" or absent, I get the previous behavior (assembly load loop when loading a plugin for a second time from a different location). I think it is all good from my end. Let me know if you have any comments on the changes (for example, on the naming of the config option). Once I get a thumbs-up from you, I'll go ahead and submit a PR. |
Glad to hear it is working for you. I took a look at your branch - looking good. Some comments:
|
Ok - changes made: |
For the test, it should still always add the property, but set it to RuntimeConfig.FromFile(app.RuntimeConfigJson)
.WithProperty("System.Runtime.InteropServices.CppCLI.LoadComponentInIsolatedContext", load_isolated.ToString())
.Save(); Otherwise, seems good. Please put up a PR whenever you are ready. |
Fixed. PR is here: #105337 Thanks for your help! |
I have a framework using C++/CLI to load managed plugins within a native application.
This change to loading assemblies in the default ALC:
#66486
has broken my ability to load multiple plugins.
Is there any way to revert to the previous isolated assembly loading behavior?
The text was updated successfully, but these errors were encountered: