Announcement tells us:
Assembly unloadability is a new capability of AssemblyLoadContext. This new feature is largely transparent from an API perspective, exposed with just a few new APIs. It enables a loader context to be unloaded, releasing all memory for instantiated types, static fields and for the assembly itself. An application should be able to load and unload assemblies via this mechanism forever without experiencing a memory leak.
Also, this design notes has mentioning of "statics".
I have tried this straightforward test:
static void Main()
{
Proxy.X = 15;
var alc = new AssemblyLoadContext("MyTest", true);
var asm = alc.LoadFromAssemblyName(typeof(Program).Assembly.GetName());
var proxy = (Proxy)asm.CreateInstance(typeof(Proxy).FullName);
Console.WriteLine(proxy.Increment());
}
class Proxy
{
public static int X;
public int Increment() => ++X;
}
It outputs "16", which means that isolation doesn't work.
My goal is to unit-test class static members which can throw exceptions. Usual tests can affect each other's behavior by triggering type initializers, so I need to isolate them in the cheapest possible way. Test should run on .NET Core 3.0.
Is it right way to do it, and can AssemblyLoadContext help with it?
Yes, it does isolate static variables.
If we look at the newest design notes, we see this addition:
LoadFromAssemblyName
This method can be used to load an assembly into
a load context different from the load context of the currently
executing assembly. The assembly will be loaded into the load context
on which the method is called. If the context can't resolve the
assembly in its Load method the assembly loading will defer to the
Default load context. In such case it's possible the loaded assembly
is from the Default context even though the method was called on a
non-default context.
Calling this method directly on the AssemblyLoadContext.Default will
only load the assembly from the Default context. Depending on the
caller the Default may or may not be different from the load context
of the currently executing assembly.
This method does not "forcefully" load the assembly into the specified
context. It basically initiates a bind to the specified assembly name
on the specified context. That bind operation will go through the full
binding resolution logic which is free to resolve the assembly from
any context (in reality the most likely outcome is either the
specified context or the default context). This process is described
above.
To make sure a specified assembly is loaded into the specified load
context call AssemblyLoadContext.LoadFromAssemblyPath and specify the
path to the assembly file.
It's little bit frustrating, because now I need to determine the exact location of the assembly to load (there's no easy way to "clone" already loaded assemblies).
This code works (outputs "1"):
static void Main()
{
Proxy.X = 15;
var alc = new AssemblyLoadContext("MyTest", true);
var asm = alc.LoadFromAssemblyPath(typeof(Program).Assembly.Location);
var proxy = asm.CreateInstance(typeof(Proxy).FullName);
Console.WriteLine(proxy.GetType().GetMethod("Increment").Invoke(null, null));
}
class Proxy
{
public static int X;
public static int Increment() => ++X;
}
(Notice, now we can't cast to Proxy class, because it is different from the run-time class of proxy variable, even being the same class...)
Related
I created a wrapper for an existing project that currently only officially supports Lua for server-side coding, the server is coded in C# so it wasn't a real issue to use reflection to access the methods provided in Lua.
I'm loading "scripts" into a new AppDomain, which works just fine, however, as soon as I'm passing a delegate (delegates are used as event handlers by the server code, passed through one of the methods), the host domain attempts to load the script's assembly, which defeats the original purpose of separating the scripts from the host domain so they could be unloaded.
if I do provide the host domain with the assembly, everything works fine, until I edit the code and add/remove/modify the delegates, which then breaks every reference to the calling methods since it relies on an older copy of the assembly, as I'm loading it from a byte array so the assembly file could be modified at run time.
How can I pass delegates without having to load the assembly passing them into the host AppDomain so the scripts could remain truly isolated from the hosting AppDomain and be unloaded/loaded at will?
EDIT: Using the classes SeparateAppDomain and MefLoader from the Plugin framework project on https://www.codeproject.com/Articles/831823/Plugin-framework I load scripts like so:
Load() on the hosting domain
MefLoader mefLoader = SeparateAppDomain.CreateInstance<MefLoader>(path, path);
List<IServerScript> scripts = mefLoader.Load<IServerScript>();
foreach (IServerScript script in scripts)
{
ServerScript ss = ((ServerScript)script);
ss.CreateProxy(AppDomain.CurrentDomain);
}
In the ServerScript class (which is loaded on the new AppDomain by the MefLoader class)
private Wrapper w = null;
internal void CreateProxy(AppDomain HostDomain)
{
Type wrappertype = typeof(Wrapper);
w = (Wrapper)HostDomain.CreateInstanceAndUnwrap(wrappertype.Assembly.FullName, wrappertype.FullName, false, BindingFlags.CreateInstance, null, new object[] { }, CultureInfo.InvariantCulture, null);
}
w is the way back to the hosting domain, which handles everything regarding reflection to the server assembly.
The issue is reproduced like so:
In ServerScript
public void Test(Delegate d)
{
if (w != null) w.Test(d);
}
In any class inheriting ServerScript that would be loaded by MefLoader
Test(new Action(() => { });
In Wrapper
public void Test(Delegate d)
{
}
The hosting domain does not attempt to load the script assembly until the call w.Test(d) is made in ServerScript.
EDIT 2: After further tests, the cause isn't the separate appdomains, but the separate assemblies, unless I pass a delegate that's defined in the wrapper assembly I end up with the issue described above, this is more than likely why the wrapper attempts to load the script assembly, is there any way I could pass delegates (would often be an Action<> with a varying amount of parameters depending on the use case) from the script assembly to the wrapper assembly without loading the script assembly into the wrapper's domain?
So lately I've been working on a project where the application (or executable,whatever you wish to call it) needs to be able to load and unload assemblies not found within the executable's folder at all. (might even be another drive)
For the sake of an example , I want to be able to have my application at D:\AAA\theAppFolder , and the assemblies of the DLL files at C:\BBB\Assemblies
Looking thoroughly , I found out AppDomain allow the ability to unload themselves and any attached assemblies , so I figured I'd give it a shot, however there seems to be an issue after a few hours worth of attempts : AppDomains cannot look anywhere outside the application base.
According to AppDomain's documentary (and my own experience) you cannot set the PrivateBinPath outside of ApplicationBase , and if I set the ApplicationBase outside of the drive the application is at (via AppDomainSetup) , I get System.IO.FileNotFoundException complaining it can't find the application itself.
Because of that I can't even reach the phase where I can use the AssemblyResolve ResolveEventHandler to attempt to get the assembly using MarhsalByRefObject inheriting classes...
Here's a few snippets of code related to what I am currently attempting
internal class RemoteDomain : MarshalByRefObject
{
public override object InitializeLifetimeService() //there's apparently an error for marshalbyref objects where they get removed after a while without this
{
return null;
}
public Assembly GetAssembly(byte[] assembly)
{
try
{
return Assembly.Load(assembly);
}
catch (Exception e)
{
Console.WriteLine(e);
}
return null;
}
public Assembly GetAssembly(string filepath)
{
try
{
return Assembly.LoadFrom(filepath);
}
catch (Exception e)
{
Console.WriteLine(e);
}
return null;
}
}
public static Assembly LoadAssembly(string modName, BinBuffer bb)
{
string assembly = pathDirTemp+"/"+modName+".dll";
File.WriteAllBytes(assembly, bb.ReadBytes(bb.BytesLeft()));
RemoteDomain loader = (RemoteDomain)modsDomain.CreateInstanceAndUnwrap(typeof(RemoteDomain).Assembly.FullName, typeof(RemoteDomain).FullName);
return loader.GetAssembly(assembly);
}
To be as specific as I can : Is there any way to get an unloadable AppDomain to load an assembly that is not within the application's base folder?
Each AppDomain has it's own base directory and is not constrained at all by the main application base dir (unless it is the main AppDomain of the application). So you can achieve what you want using AppDomains.
The reason your approach doesn't work is that you are passing Assembly objects between AppDomains. When you call any of the GetAssembly methods, the assembly will be loaded in the child AppDomain but when the method returns, the main AppDomain will try to load the Assembly as well. Of course, the assembly will not be resolved because it is not in the main AppDomains's base dir, private paths or the GAC.
So in general you should never pass Type or Assembly objects between AppDomains.
A simple way to load assemblies without leaking them to the main AppDomain can be found in this answer.
Of course to make your application work with assemblies loaded in child AppDomains you will have to make MarshalByRefObject derived classes that will be your access point between AppDomains.
Maybe you need to use global variable so if you use global variable to fix the problem you can declare readonly global variable for example:
public static string a = "Moosaie";
Convert it to
public static readonly a = "Moosaie";
Anyway you can not use global dynamic value variable for CLR assembly.
I would like to execute certain code in a class library when it is instantiated from another assembly. Is there an entry point or bootstrap for a class library?
I thought that a static method Main would do the trick but I was wrong.
Applications for this might be configuring and instantiating a logger singleton, unhandled exception handler, etc.
A library as it is, has not an starting point. When you are instancing a class of a library the first instruction you call is the constructor of the class (new) and its base constructors if they are on the constructor definition.
AppDomain.AssemblyLoad Event which occurs when an assembly is loaded. Probably that can be used to call an initialize method in your class library.
public static void Main()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyLoad += new AssemblyLoadEventHandler(MyAssemblyLoadEventHandler);
}
static void MyAssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("ASSEMBLY LOADED: " + args.LoadedAssembly.FullName);
//If this is the assembly that you want to call an initialize method..
}
Below are two similar threads
how to write class lib's assembly load/init event handler
.Net: Running code when assembly is loaded
The best and safe way is to design your library so that the caller will initialize your library when they know they can.
When an assembly is loaded, the CLR employes a lot of machinery to have the job done, starting from how the inner platform is designed to load modules up to the CLR itself. Each involved actor has their own limitations.
Executing code when a module is loaded is not the best practice on Win32 for the same reason: you cannot know what your caller is doing; also, the changes your doing could possibly alter the current AppDomain, but may not plbe propagated in all the other AppDomain of the application.
A conscious initialization method is the cleanest way to let the caller to control the initialization of your assembly.
All the other answers partially address the issue, but could introduce unwanted side effects and non deterministic behaviors.
Have you looked into the PreApplicationStartMethodAttribute?
using System.Web;
[assembly: PreApplicationStartMethod(typeof(ClassLibrary.Startup), "Start")]
namespace ClassLibrary
{
public class Startup
{
public static void Start()
{
// do some awesome stuff here!
}
}
}
More detail: http://dochoffiday.com/professional/simulate-application-start-in-class-library
I am currently writing a framework for MutationTesting. The code is almost complete, but there is a tiny bit which (after spending half a day on it) I cannot figure out:
via reflection I would like to execute a method "TestMethod" which is inside the class "TestClass". The project in which "TestClass" is, references an essembly, lets call it "Proband.dll".
The "TestMethod" creates an object of some type inside the Proband.dll and executes a method on that object.
In order to clarify a little:
TestClass - is a class that contains unit tests.
TestMethod - is a single unit test.
Proband.dll - contains the method/class to test.
Before executing the TestMethod, I already successfully disassembled, mutated and reassembled the Proband.dll. So now I have a new "Proband.dll" which shall be taken by the TestClass instead!
The problem is that the TestClass is already in the middle of execution. I was thinking whether it is possible to create an AppDomain in which the new Proband.dll will be loaded and inside the fresh AppDomain, the TestMethod will be executed.
I have already created this AppDomain and successfully loaded the new Proband.dll into it, however, I do not know how to execute the TestMethod inside this new AppDomain. Also I do not know whether this is going to "replace" the old Proband.dll for the test method.
Here is my TestClass:
[TestClass()]
public class TestClass
{
[TestInitialize()]
public void MyTestInitialize()
{
// code to perform the mutation.
// From here, the "TestMethod" should be called with
// the new "Proband.dll".
}
[TestMethod()]
public void TestMethod()
{
// test code calling into Proband.dll
}
}
Does anyone have an idea of how this could be achieved? Or any clue or keyword?
Thanks,
Christian
This is probably overkill, but check out my answer:
Static Fields in AppDomain
You'll want to create the TestClass in the AppDomain that you've loaded the Proband.dll in, and use a MarshalByRef to keep the class in the AppDomain (so you can unload it later).
Here is more about what I did (not exact, as i'm changing it to match more what you need).
// Create Domain
_RemoteDomain = AppDomain.CreateDomain(_RemoteDomainName,
AppDomain.CurrentDomain.Evidence,
AppDomain.CurrentDomain.BaseDirectory,
AppDomain.CurrentDomain.BaseDirectory,
true);
// Load an Assembly Loader in the domain (mine was Builder)
// This loads the Builder which is in the current domain into the remote domain
_Builder = (Builder)_RemoteDomain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName, "<namespace>.Builder");
_Builder.Execute(pathToTestDll)
public class Builder : MarshalByRefObject
{
public void Execute(string pathToTestDll)
{
// I used this so the DLL could be deleted while
// the domain was still using the version that
// exists at this moment.
Assembly newAssembly = Assembly.Load(
System.IO.File.ReadAllBytes(pathToTestDll));
Type testClass = newAssembly.GetType("<namespace>.TestClass",
false, true);
if (testClass != null)
{
// Here is where you use reflection and/or an interface
// to execute your method(s).
}
}
}
This should provide you the solution you need.
I have an asp.net application. I want to load some assemblies dynamically.
this is my code on application start
protected void Application_Start(Object sender, EventArgs e)
{
LoadPrivateAssemblies();
}
private static void LoadPrivateAssemblies()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
Assembly.Load("MyDLL");
}
static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
{
//loads and returns assembly successfully.
}
this code works fine except when a nested c# code calls a class from my dynamic dll Inside an asp.net page (not code-behind)
sample :
<%if(MyDLL.TestObject.Value){%>white some ting<%}%>
what should I do now ?
I think If I know When a new AppDomain is created, it may solve my problem.
I really think you are barking up the wrong tree here.
Assembly.Load(byte[]) does "persist" the assembly in the app domain - otherwise, what else would it be doing?
To illustrate the fact it does, try this:
Create a solution with one console application, and one class library, named OtherAssembly.
In the class library, OtherAssembly, add a single class:
namespace OtherAssembly
{
public class Class1
{
public string HelloWorld()
{
return "Hello World";
}
}
}
In the console application, use this as your program:
public class Program
{
static void Main(string[] args)
{
try
{
using (var fs = new FileStream("OtherAssembly.dll", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var buffer = new byte[fs.Length];
// load my assembly into a byte array from disk
fs.Read(buffer, 0, (int) fs.Length);
// load the assembly in the byte array into the current app domain
AppDomain.CurrentDomain.Load(buffer);
}
// get my type from the other assembly that we just loaded
var class1 = Type.GetType("OtherAssembly.Class1, OtherAssembly");
// create an instance of the type
var class1Instance = class1.GetConstructor(Type.EmptyTypes).Invoke(null);
// find and invoke the HelloWorld method.
var hellowWorldMethod = class1.GetMethod("HelloWorld");
Console.WriteLine(hellowWorldMethod.Invoke(class1Instance, null));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
Console.ReadLine();
}
}
}
Don't reference OtherAssembly from your main program assembly, instead, compile the solution and manually drop OtherAssembly.dll in the main program's bin folder.
Run the main program. It outputs "Hello World", which it can only have done if the assembly was loaded and retained in memory. You'll note I've been very careful not to give Visual Studio or C# any hint to load this OtherAssembly. No reference was added, the type is not explicitly referenced in C#.
You need to look again at your problem.
[EDIT: in response to you commenting on the fact this is not an ASP.NET application]
OK - I've moved my main program to an ASP.NET web page and tried accessing the assembly both from code behind and the markup - and it works in both situations. I'm sure you are missing something here - it just doesn't make sense that the behaviour of this method - whose job it is to load an assembly into the current app domain - is different in an ASP.NET scenario.
It seems to me there are at least two things to investigate:
If it really is a new app domain being created on each call, it would only be because of an unhandled exception in your application. The quickest way to diagnose this is to use SysInternals Process Explorer. Fire it up - add .Net -> Total AppDomains to the list of columns and watch the app domain count for your process (IIS worker process or web dev) as you run your app. If this increases on each call, you've got unhandled exceptions tearing down one AppDomain and forcing another to be created.
Are you sure this assembly is really being loaded? When you pass the byte array to Assembly.Load, try also writing it out to disk using FileStream.Write. Open the file in Reflector or some similar tool. Is this the assembly you expected it to be. Is it really being loaded at all, or is it an empty/corrupt array?
I'm not trying to be argumentative, but this really feels like you are looking in the wrong place for the cause of the problem.
I think If I know When a new AppDomain is created, it may solve my problem
You should use AssemblyLoad event. AssemblyResolved occurs when the resolution of assembly is fails
I found that the problem is because Assembly.Load(bytes); does not persist the assembly in appdomain. does any body know how to persist loaded assembly using Assembly.Load(bytes); in appdomain ?
finally I decided to switch to LoadFile method instead of load.
EDIT
Finally I switched from Assemly.Load(byte[]) to Assembly.LoadFile(string).
But it did not correct the problem itself. I have added <%#Assembly name="MyDLL"%> To All of my ASPX files that has markup C# code emebeded.
And This Solved my Problem.
Thanks to your answers. I have voted up answers that helped me, but I can not accept your solutions because no one is not complete enough.