Assembly.LoadFrom() or Assembly.Load() being able to delete file

979 views Asked by At

My goal: I am writing a vsix (Visual Studio extension) where i want to compile a project, then load the resulting .dll and inspect it via reflection. Due to how the code is written, i am unable to use ReflectionOnlyLoad(). If i simply do Assembly.Load then the file is locked until the user restarts the whole IDE.

I am trying to setup a separate AppDomain based on the samples i found online.

The gist of it is: 1. I created a Proxy class that would marshal the data across AppDomain instances:

internal class AppDomainProxy : MarshalByRefObject
    {
        public Assembly GetAssembly(string assemblyPath)
        {
            return Assembly.LoadFile(assemblyPath);
        }
}

I then create an instance of it:

var domaininfo = new AppDomainSetup { ApplicationBase = System.Environment.CurrentDirectory, ShadowCopyDirectories = "true", ShadowCopyFiles = "true", LoaderOptimization = LoaderOptimization.MultiDomainHost };

            var adevidence = System.AppDomain.CurrentDomain.Evidence;
            var domain = System.AppDomain.CreateDomain("reflection", adevidence, domaininfo);

             var proxyType = new AppDomainProxy().GetType();

            var proxyInstance = (AppDomainProxy)domain.CreateInstanceFromAndUnwrap(proxyType.Assembly.Location, proxyType.FullName);

            var loadedAssembly = (proxyInstance as AppDomainProxy).GetAssembly(this._assemblyLocation);

This fails to cast my transparent proxy to my AppDomainProxy type. To workaround this, one can easily supply an assembly resolver like so:

            this.domain.AssemblyResolve += CurrentDomainOnAssemblyResolve;

private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
        {
            var loadedAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();

            foreach (var assembly in loadedAssemblies)
            {
                if (assembly.FullName == args.Name)
                {
                    return assembly;
                }
            }

            return null;
        }

This works fine, my proxy is cast. However, when i call my method, the CurrentDomainOnAssemblyResolve is invoked again, suggesting to me that Assembly property is not really serializable and so .net is simply trying to load the Assembly on the original side, thus resulting in the same problem as Assembly.Load. This is easy to see because simple Microsoft samples like return AppDomain.Current.FriendlyName; work just fine.

UPDATE As a workaround, i just moved my code that needed the Assembly to run on the other side (inside the domain) and then return the string back which marshals fine. I will keep the question open, though, because i want to know if there is a solution for the actual issue.

1

There are 1 answers

3
Luaan On BEST ANSWER

Well, of course you need to move the code that needs to work with the assembly to the separate AppDomain. There's no magic involved - if you need to work with a type from a referenced assembly, you need to load that assembly.

There's two main ways of marshalling objects across AppDomain boundaries (and other remoting scenarios): either you make a copy of the object, or you marshal all the method calls. Both are very tricky - you must make sure you never leak the types that are in the referenced assembly, otherwise you need to load it into both domains. In your case, you can't marshal the method calls because Assembly is not (and cannot be) a MarshalByRefObject - your proxy must return a real Assembly object, and cannot create the marshalling proxy.

To get proper isolation, you must avoid leaking any types from assemblies you don't want to share, as well as non-marshallable types that may expose any of those. This usually means sticking to primitives and types from a shared library if you can afford that. Keep a nice tight interface, and try not to exploit the "automatic" marshalling too much - returning an automatic proxy to a complex object is convenient, but makes it much harder to understand the scope of the interface. Does the object have a method that returns a type from the non-shared assembly? Too bad, you need to load it in your domain as well.

In general, working with multiple AppDomains that actually interoperate (and the related .NET remoting) is a huge pain. It's complex, prone to errors and rather hard to get right. You can read whole books on the subject. There's a reason why using them is no longer recommended - and why they aren't supported in PCLs (and for a long time, haven't been supported in Mono). Are they still useful? Yeah. Software isolation has very nice benefits. But you need to be really careful not to mess things up :)