If you create a domain and load the assembly into it, and then using the GetAssemblies method, get the assemblies, then by unloading the domain, the assembly remains in the main application domain and nothing can be done with it, for example, you cannot delete it.

How can you use assembly methods from a foreign domain, while unloading the domain, unload and assembly from the application?

Here is the code that I am doing:

 using System; using System.IO; using System.Reflection; namespace Test { internal static class Program { private static void Main() { var appDomain = AppDomain.CreateDomain( "test" ); AssemblyLoader.Run(appDomain); Console.WriteLine("Сборка загружена"); foreach ( var assembly in appDomain.GetAssemblies () ) { Console.WriteLine ( assembly.GetName () ); } Console.ReadLine(); AppDomain.Unload( appDomain ); Console.WriteLine("Домен выгружен"); //Не удается удалить сборку из каталога, она не выгрузилась! Console.ReadLine(); } } public static class AssemblyLoader { public static void Run( AppDomain appDomain ) { appDomain.DoCallBack( Invoke ); } private static void Invoke() { Assembly.LoadFile( Path.Combine( Environment.CurrentDirectory, "test.dll" ) ); } } 

}

  • 2
    All code that somehow works with the loaded assembly should be executed in a separate domain. By returning an instance of the Assembly class representing the loaded assembly to the main domain, you are actually loading your assembly and into it. - PetSerAl
  • @PetSerAl, I understood that, thanks. Is it possible to somehow use assembly methods from another domain so that it stays there? - anweledig

1 answer 1

If you transfer an object from another domain, you thereby load it into your domain, which you just want to avoid. The solution may be to pass primitive types. In order not to limit yourself to simple callbacks, you need to use MarshalByRefObject (they are not copied between domains, but are executed in the domain where they are created).

So, here is the working code.

This is the DomainsPlugin.dll plugin:

 namespace DomainsPlugin { public class Plugin { public IEnumerable<AssemblyName> GetNames() { return AppDomain.CurrentDomain.GetAssemblies().Select(asm => asm.GetName()); } } } 

Nothing special, just a working method.

Now the main program:

 namespace DomainsMain { class Program { static void Main(string[] args) { // в этом каталоге бежит программа var executableDir = Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location); // это каталог, где лежит плагин. // у вас он будет лежать, понятно, в другом месте var pluginDir = Path.GetFullPath(Path.Combine( executableDir, "..", "..", "..", "DomainsPlugin", "bin", "Debug")); // оригинал плагина var pluginSourceFile = Path.Combine(pluginDir, "DomainsPlugin.dll"); // а сюда мы его скопируем, и отсюда будем загружать var pluginWorkingFile = Path.Combine(executableDir, "DomainsPlugin.dll"); // копируем File.Copy(pluginSourceFile, pluginWorkingFile, overwrite: true); // окей, теперь новый домен var pluginDomain = AppDomain.CreateDomain("Plugin"); // теперь, создаём экземпляр нашего класса в новом домене // у нас на него реально лишь прокси-объект var resident = (Resident)pluginDomain.CreateInstanceAndUnwrap( Assembly.GetExecutingAssembly().FullName, "DomainsMain.Resident"); // получили данные resident.Obtain(); // выводим их foreach (var r in resident.Result) Console.WriteLine(r.ToString()); // выгружаем домен... AppDomain.Unload(pluginDomain); // ... и удаляем плагин File.Delete(pluginWorkingFile); } } // простой класс, будет выполняться в другом домене public class Resident : MarshalByRefObject { public List<AssemblyName> Result; public void Obtain() { // загружаем библиотеку var asm = Assembly.LoadFrom("DomainsPlugin.dll"); var type = asm.GetType("DomainsPlugin.Plugin"); // и вызываем функцию из неё через рефлексию // можно было бы закастить к интерфейсу, если объявить его в // этой или другой общей dll-ке var p = Activator.CreateInstance(type); var method = type.GetMethod( "GetNames", BindingFlags.Instance | BindingFlags.Public); var result = (IEnumerable<AssemblyName>)method.Invoke(p, null); // сохраняем результат. это поле будет доступно из первоначального домена Result = result.ToList(); } } } 

On my machine, from under Visual Studio, it produces:

mscorlib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089
Microsoft.VisualStudio.HostingProcess.Utilities, Version = 14.0.0.0, Culture = neutral, PublicKeyToken = b03f5f7f11d50a3a
DomainsMain, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null
System.Core, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089
DomainsPlugin, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null


Update: the resident function ( Obtain() ) may well return a value. But this value must be serialized in order to “squeeze” between domains.

For our example, correct the Resident class:

 public class Resident : MarshalByRefObject { public List<AssemblyName> Obtain() // <-- поменяли возвращаемый тип { // и убрали свойство Result // загружаем библиотеку var asm = Assembly.LoadFrom("DomainsPlugin.dll"); var type = asm.GetType("DomainsPlugin.Plugin"); // и вызываем функцию из неё через рефлексию // можно было бы закастить к интерфейсу, если объявить его в // этой или другой общей dll-ке var p = Activator.CreateInstance(type); var method = type.GetMethod( "GetNames", BindingFlags.Instance | BindingFlags.Public); var result = (IEnumerable<AssemblyName>)method.Invoke(p, null); return result.ToList(); } } 

Now the call from the main code looks like this:

 // получили данные var result = resident.Obtain(); // выводим их foreach (var r in result) Console.WriteLine(r.ToString()); 
  • one
    Very cool example, thanks. Unfortunately, there is no way to check, but I want to know what will happen if I try to return this list from the Obtain method? I understood approximately that in your example the transfer occurs by serialization, for I don’t see another explanation for this choice, is there a counter-question, is the speed of this interaction tolerated? Thanks again, you really helped! - anweledig
  • 2
    @anweledig: Please! Returning a value from a function is also possible, added an answer with an example. // Under the hood, there are two options. If the passed class is inherited from MarshalByRefObject , then it is not passed directly, but a proxy object is constructed that refers in the target domain to the domain where the original exists. If the class is not inherited from MarshalByRefObject , then serialization occurs across the domain boundaries, and in fact we have a copy of the object in our hands (and this means that if the object changes later, our copy will not be affected). - VladD
  • 2
    Serialization, of course, is significantly slower than the normal exchange of values. Whether speed is tolerant depends on the task. It seems normal for business logic, but it may not be enough for graphics and all that. In any case, it is necessary to test the speed of work, and try to reduce the amount of information transmitted across the domain boundaries. - VladD