-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
Good morning. I want to reload a dll at runtime but I can't. I have two programs lets name them host and plugin. Host compiles to an executable and plugin compiles to a dll. Host loads a function from the plugin at runtime and i make changes to the plugin and recompile it. And then in the host i want to reload the plugin dll and want to use the updated function without needing the relaunch the host program. i have created a small example where this issue exist. For reference here is the code:
Host.cs:
using System;
using System.Reflection;
using System.Runtime.Loader;
namespace Host {
public class PluginAssemblyLoadContext : AssemblyLoadContext {
public PluginAssemblyLoadContext() : base(true) { }
}
public class HostState {
public Assembly pluginAssembly;
public PluginAssemblyLoadContext loadContext;
public Action pluginExecute;
}
public static class Host {
public const string PLUGIN_PATH = "../plugin/bin/Debug/net9.0/plugin.dll";
public static void Main() {
HostState host = new HostState();
host.loadContext = new PluginAssemblyLoadContext();
ReloadDll(host);
Console.WriteLine("Press E to execute the plugin, R to reload, or Q to quit.");
for (;;) {
var key = Console.ReadKey(true);
if (false) {
} else if (key.Key == ConsoleKey.E) {
host.pluginExecute.Invoke();
} else if (key.Key == ConsoleKey.R) {
ReloadDll(host);
} else if (key.Key == ConsoleKey.Q) {
return;
}
}
}
public static void ReloadDll(HostState host) {
host.pluginAssembly = null;
host.pluginExecute = null;
GC.Collect();
GC.WaitForPendingFinalizers();
host.loadContext?.Unload();
GC.Collect();
GC.WaitForPendingFinalizers();
host.loadContext = new PluginAssemblyLoadContext();
host.pluginAssembly = host.loadContext.LoadFromAssemblyPath(Path.GetFullPath(PLUGIN_PATH));
Type type = host.pluginAssembly.GetType("Plugin");
MethodInfo methodInfo = type.GetMethod("Execute", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, []);
if (methodInfo != null) {
host.pluginExecute = methodInfo.CreateDelegate<Action>();
}
}
}
}Plugin.cs:
using System;
public static class Plugin {
public static void Execute() {
Console.WriteLine("1");
}
}The Scenario
I launch the host program. I press E to execute plugin. I got the output "1". I go to Plugin.cs. I change Execute() function to output "2" instead of "1". I go back to the terminal where host program is running. I press R and then I press E.
What Happens
I got the output "1" again.
What should have happened
I should have got the output "2" instead.
What I have tried
- I tried to set every reference to null which i obtained from the PluginAssemblyLoadContext as mentioned in the third remark of ms docs
- I tried to change the assembly version when i build the plugin.dll but that didn't help either.
- Called
GC.Collect()andGC.WaitForPendingFinalizers()to make sure every reference gets garbage collected.
Why I want to do this
I have a game engine and i want to be able reload the game dll without needing to restart the editor of the engine. So in this case host would be the editor of the engine and plugin would be the game.
Specs
- cpu: x86-64
- os: debian sid
- dotnet sdk version: 9.0.305
-
Any idea how to reload a dll at runtime? Any help would be appreciated.
Reproduction Steps
clone the repo and then follow the scenario above.
Expected behavior
to be able to execute the new function in recompled plugin.dll
Actual behavior
still old function is being called.
Regression?
idk
Known Workarounds
i know no workarounds
Configuration
dotnet --version: 9.0.305- os: debian sid
- cpu architecture: x86-64
- i dont think this problem is specific to this configuration tho i have not tried other configurations.
Other information
i think the problem is the AssemblyLoadContext.Unload() function doesn't unload the assembly thats loaded from it.
Metadata
Metadata
Assignees
Type
Projects
Status