Shadow copy so as not to block the assembly

I created a test library

public class Test { public int Add(int val1, int val2) { return val1 + val2; } } 

And the project for calling it:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.IO; namespace Loader { class Program { static void Main(string[] args) { String path = @"...Lib.dll"; // path to lib var name = Path.GetFileName(path); AppDomainSetup setup = new AppDomainSetup { ApplicationBase = @"...", // directory where Lib.dll is ShadowCopyFiles = "true", ShadowCopyDirectories = @"..."// directory where Lib.dll is }; var appdomain = AppDomain.CreateDomain("Loader." + name, null, setup); Assembly ass = Assembly.LoadFile(path); // <--- I think here is the problem, here where assembly is locked Assembly assembly = appdomain.Load(ass.FullName); dynamic a = assembly.CreateInstance("Lib.Test"); Console.WriteLine(a.Add(1, 5)); } } } 

Please help find what I did wrong? Why is my build blocked?

EDIT: with the name of the hard line:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.IO; namespace Loader { class Program { static void Main(string[] args) { AppDomainSetup ads = new AppDomainSetup(); String fullPath = @"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Lib.dll"; ads.ShadowCopyFiles = "true"; ads.ApplicationName = "AppName"; ads.ShadowCopyDirectories = Path.GetDirectoryName(fullPath); //ads.ApplicationBase = Path.GetDirectoryName(fullPath); //ads.PrivateBinPath = Path.GetDirectoryName(fullPath); ads.CachePath = @"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Cache\"; AppDomain ad = AppDomain.CreateDomain("myName" + ads.ApplicationName, null, ads); ad.AssemblyResolve += new ResolveEventHandler( ad_AssemblyResolve ); Console.WriteLine(ad.ShadowCopyFiles); Console.WriteLine(ad.SetupInformation.ShadowCopyDirectories); try { //Assembly assembly = ad.Load(AssemblyName.GetAssemblyName(fullPath)); //dynamic obj = ad.CreateInstanceAndUnwrap(assembly.GetName().Name, "Lib.Test"); dynamic obj = ad.CreateInstanceAndUnwrap("Lib", "Lib.Test"); Console.WriteLine(obj.Add(1, 7)); Console.ReadKey(); Console.WriteLine(obj.Add(1, 90)); } catch( Exception e) { Console.WriteLine( e.Message ); } } static Assembly ad_AssemblyResolve(object sender, ResolveEventArgs args) { return Assembly.LoadFile(@"c:\users\myuser\documents\visual studio 2010\Projects\ShadowCopy\Loader\bin\Debug\Lib.dll"); } } } 

Interestingly, the cached assebly is also blocked.

EDIT 2:

Here is a line of code to lock the assembly

 Console.WriteLine(obj.Add(1, 7)); 

So, as soon as the method in Lib.Test is available, assenbly will be blocked.

What could be the solution?

+6
source share
2 answers

You load the assembly into your main AppDomain when you run Assembly.LoadFile() , which locks the file. If you want to load it directly into your AppDomain, you need to use AppDomain.Load() directly, allowing AppDomain to allow it based on the parameters you specify. Please note: since the assembly is not loaded into your main AppDomain, this type is not available from your Main method. Your Lib.Test must receive from MarshalByRefObject so that the marshalling system can create a proxy server (which will then be migrated to a dynamic proxy when you assign it to a dynamic proxy.)


EDIT:

I further thought that the problem is that to call a method in a dynamic proxy, it must Reflect on the object (which in itself is a transparent proxy for the instance in your other AppDomain.) If it ends up reflecting the type of the source class (and not "hidden" transparent proxy type), this will force it to load a copy of the assembly in the local AppDomain (thus blocking it). This is one of the reasons why when using AppDomain, you usually pass an object to an interface defined in a separate assembly, which can refer to both the "main" application and the hosted AppDomain. Each of them will receive its own copy of the interface assembly, but now the implementation assembly is isolated.

+1
source

You have two options: you need to find the assembly name before starting it and hard-code it, or if you do not know what it is before starting, you need to write a second library in which you know FullName and load it into AppDomain. The second library will be one class that has one function

 public Assembly GetAssemblyForOtherAppDomain(string path) { return Assembly.LoadFile(path) } 

This should load your target in the new AppDomain without blocking it, since you should open it with a shadow copy.

EDIT: about why your method does not work, where you are right, that it blocks it, and after the assembly is loaded into AppDomain, it cannot be unloaded.

0
source

Source: https://habr.com/ru/post/891469/


All Articles