C
C#2y ago
tzainten

❔ Confused about Assembly.Load (Hotloading CSharp Assemblies)

I want to be able to load/unload user made C# libraries during runtime. However, these user libraries rely on one C# FunctionLibrary.dll file I made that will basically hold functions that call Native C++ functions. FunctionLibrary.dll is structured like this:
C#
namespace FunctionLibrary;

public static class Module
{
public static bool Test = false;
}
C#
namespace FunctionLibrary;

public static class Module
{
public static bool Test = false;
}
UserLibrary.dll is structured like this:
C#
using FunctionLibrary;

public class MyLibrary
{
public static void OnStartup()
{
Console.WriteLine(FunctionLibrary.Test);
}
}
C#
using FunctionLibrary;

public class MyLibrary
{
public static void OnStartup()
{
Console.WriteLine(FunctionLibrary.Test);
}
}
FunctionLibrary.dll and UserLibrary.dll are loaded using Assembly.Load, which looks something like this:
C#
public unsafe class Program
{
[UnmanagedCallersOnly]
public static void Main()
{
try
{
Assembly funcLibrary = Assembly.Load(ToMemoryStream(File.OpenRead("FunctionLibrary.dll")));
Type? module = funcLibrary.GetType("FunctionLibrary.Module");
FieldInfo testField = module?.GetField("Test", BindingFlags.Static | BindingFlags.Public);
testField.SetValue(null, true); // Set FunctionLibrary.Module.Test = true

Assembly userLibrary = Assembly.Load(ToMemoryStream(File.OpenRead("UserLibrary.dll")));
Type? userModule = userLibrary.GetType("MyLibrary.MyModule");
MethodInfo entryPoint = userModule?.GetMethod("OnStartup", BindingFlags.Static | BindingFlags.Public);
entryPoint.Invoke(null, null); // Print out the value of FunctionLibrary.Module.Test, expected to == true
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
C#
public unsafe class Program
{
[UnmanagedCallersOnly]
public static void Main()
{
try
{
Assembly funcLibrary = Assembly.Load(ToMemoryStream(File.OpenRead("FunctionLibrary.dll")));
Type? module = funcLibrary.GetType("FunctionLibrary.Module");
FieldInfo testField = module?.GetField("Test", BindingFlags.Static | BindingFlags.Public);
testField.SetValue(null, true); // Set FunctionLibrary.Module.Test = true

Assembly userLibrary = Assembly.Load(ToMemoryStream(File.OpenRead("UserLibrary.dll")));
Type? userModule = userLibrary.GetType("MyLibrary.MyModule");
MethodInfo entryPoint = userModule?.GetMethod("OnStartup", BindingFlags.Static | BindingFlags.Public);
entryPoint.Invoke(null, null); // Print out the value of FunctionLibrary.Module.Test, expected to == true
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
Invoking MyLibrary.OnStartup prints false, not true. I assume I'm just going the wrong way about this, but I'm not really sure how to achieve my goal. I appreciate any help I can get on understanding what I'm doing wrong.
5 Replies
Anchy
Anchy2y ago
It might be better to use a common interface for your modules instead of a static class, my guess would be the module is referencing a different instance of the library
tzainten
tzaintenOP2y ago
Hmm alright. Is it possible to only have one instance of FunctionLibrary.dll and have everything reference that same instance?
Anchy
Anchy2y ago
what is the value when you call testField.GetValue(null)? after doing a short amount of research, it appears the libraries have to be loaded under the same appdomain to properly share the static class what are you trying to make exactly?
tzainten
tzaintenOP2y ago
Well what I'm doing is using the HostFXR API to host a C# runtime so I can interop with C++ functions. I've changed the project to output to a .exe just for testing purposes, and it prints out the expected result of "True". However, when I switch the project back to "Class Library" and try to load the library with HostFXR, it prints out this error:
"System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.IO.FileNotFoundException: Could not load file or assembly 'FunctionLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified."
"System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.IO.FileNotFoundException: Could not load file or assembly 'FunctionLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified."
I've also modified the host's code to look like this:
public static void Main()
{
try
{
FunctionLibrary.Module.Test = true;

Assembly userLibrary = LoadAssembly( @"W:\\Visual Studio 2022\\UserLibrary\\bin\\Debug\\net7.0\\UserLibrary.dll" );
Type? userModule = userLibrary.GetType( "MyLibrary.MyModule" );
MethodInfo entryPoint = userModule?.GetMethod( "OnStartup", BindingFlags.Static | BindingFlags.Public );
entryPoint.Invoke( null, null ); // Print out the value of FunctionLibrary.Module.Test, expected to == true
}
catch ( Exception ex )
{
Console.WriteLine( ex );
}
}
public static void Main()
{
try
{
FunctionLibrary.Module.Test = true;

Assembly userLibrary = LoadAssembly( @"W:\\Visual Studio 2022\\UserLibrary\\bin\\Debug\\net7.0\\UserLibrary.dll" );
Type? userModule = userLibrary.GetType( "MyLibrary.MyModule" );
MethodInfo entryPoint = userModule?.GetMethod( "OnStartup", BindingFlags.Static | BindingFlags.Public );
entryPoint.Invoke( null, null ); // Print out the value of FunctionLibrary.Module.Test, expected to == true
}
catch ( Exception ex )
{
Console.WriteLine( ex );
}
}
Which again, works if I just compile the host as a .exe and run it on it's own. But it does not work if I try compiling it as a .dll and loading it with HostFXR, it produces the error above.
Accord
Accord2y ago
Looks like nothing has happened here. I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server