Exception on receiving Assembly reference from another AppDomain
Asked Answered
K

1

7

I am loading all DLLs from a specific "extensions" directory in a new AppDomain to get some Reflection related information out of those.

This is what I'm trying:

I created a new library AssemblyProxy in my solution which just has this class:

public class AssemblyProxy : MarshalByRefObject
{
    public Assembly LoadFile( string assemblyPath )
    {
      try
      {
        return Assembly.LoadFile( assemblyPath );
      }
      catch
      {
        return null;
      }
    }
}

I make sure this DLL is present inside my "extensions" directory. Then I use the following code to load all assemblies from "extensions" directory into the new AppDomain.

foreach( string extensionFile in Directory.GetFiles( ExtensionsDirectory, "*.dll" ) )
{
        Type type = typeof( AssemblyProxy.AssemblyProxy );
        var value = (AssemblyProxy.AssemblyProxy) Domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName );

        var extensionAssembly = value.LoadFile( extensionFile );

        types.AddRange( extensionAssembly.GetTypes() );
}

Some DLLs do get loaded successfully but on some DLLs an exception is thrown like this:

Could not load file or assembly 'Drivers, Version=2.3.0.77, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

EDIT: The exception is not thrown in the new AppDomain. The DLL gets successfully loaded in the new AppDomain. The exception gets thrown as soon as the assembly reference is returned to the main/calling AppDomain. Does the main/calling AppDomain try to load the assembly on its own on just receiving the reference?

Thanks.

Karolkarola answered 2/1, 2013 at 14:49 Comment(15)
Isn't ReflectionLoadOnly sufficient for your (untold) purposes?Cadmarr
What's wrong with CreateInstanceAndUnwrap?Alvinaalvine
@ConradFrix ReflectionOnlyLoad does load the assembly in the current AppDomain, just not its dependencies. As far as I am aware you cannot unload them from the current AppDomain.Flyblown
@ConradFrix: there are other reasons to. Like just scanning the assemblies or to build plugin systems (scan assembly after plugins & unload it if there are none + unload plugins which isn't used anymore)Geisler
@all Please see the edit in my question. Why the question was closed, I have no idea. At least 24 hours should be given to make changes. :)Karolkarola
Since it's closed, do I have to ask a new question?Karolkarola
Use Assembly.LoadFrom instead of Assembly.LoadFile.Ops
@Ops Same exceptions.Karolkarola
@all I've realized that the exception is not thrown in the new AppDomain. The DLL gets successfully loaded in the new AppDomain. The exception gets thrown as soon as the assembly reference is returned to the main/calling AppDomain. Does the main/calling AppDomain try to load the assembly on its own on just receiving the reference?Karolkarola
@user1004959: WRT to last comment. That will never work. As soon as the assembly or a type of it crosses the domain boundary into the primary domain, it cannot be unloaded. You might want to rethink your approach.Ops
@Ops Why is it being loaded in primary domain at all? I just want some reflection related information available to primary domain. Like collection of all types from all assemblies of the secondary AppDomain.Karolkarola
@user1004959: Because you are returning Assembly. You will need to create a safe serializable/remotable object to transfer that info. As soon as a type crosses the boundary, you are screwed ;pOps
Why the question was closed, I have no idea. At least 24 hours... See this feature-request Let questions stay open for a minimum amount of time before being closed I guess the best thing to do to avoid a closure is to be as responsive to comments as others are to answer your question.Cynde
@Ops Are you sure there is something wrong with the Assembly object? Because if I use ReflectionOnlyLoadFrom then an object reference of same Assembly type gets returned successfully.Karolkarola
@user1004959: I can't say, never dealt with ReflectionOnly, but I would say the semantics might be different then. Have you tested it for side-effects? Remember, in that context, concrete types might not need to be created, so it does not infect the calling domain for 'executable' types.Ops
F
3

You should not return an Assembly object from the new AppDomain because this will only work if you main AppDomain has access to those assemblies which it does not since the assemblies are located in a directory that:

One way to avoid this to follow the leppie's comment and:

  1. Create a serialized type that includes the minimum information you need from the Assembly object.
  2. Add this type to new assembly that both AppDomain's have access to. The simplest way to do this by adding it to the GAC.

Another approach would be to use Mono.Cecil instead of System.Reflection. Mono.Cecil will allow you to inspect assemblies without loading them. For a very simple example look at the second half from this answer.

Foresight answered 4/1, 2013 at 10:47 Comment(2)
Are you sure there is something wrong with the Assembly object? Because if I use ReflectionOnlyLoadFrom then an object reference of same Assembly type gets returned successfully.Karolkarola
@Karolkarola I'm positive that returning an Assembly object is not what you should do. Perhaps the specific assembly was allready loaded to the main AppDomain. Add a breakpoint in the last line of the foreach, run the program, then use the QuickWatch feature of Visual Studio and evaluate System.AppDomain.CurrentDomain.GetAssemblies(). If you can see the loaded assembly then it was somehow loaded to your main AppDomain. Have you by any chance subscribed to the AssemblyResolve event while experimenting?Foresight

© 2022 - 2024 — McMap. All rights reserved.