How to load a .NET assembly for reflection operations and subsequently unload it?
Asked Answered
A

5

41

I'm writing a tool to report information about .NET applications deployed across environments and regions within my client's systems.

I'd like to read the values of assembly attributes in these assemblies.

This can be achieved using Assembly.ReflectionOnlyLoad, however even this approach keeps the assembly loaded. The issue here is that I cannot load two assemblies that have the same name from different paths, so naturally I can't compare the same application deployed in different systems.

At this point I'm assuming the solution will involve using temporary AppDomains.

Can someone detail how to load an assembly into another AppDomain, read the attributes from it and then unload the AppDomain?

This needs to work for assemblies on the file system as well as those at URL addresses.

Aboulia answered 22/10, 2008 at 11:24 Comment(0)
A
52

From the MSDN documentation of System.Reflection.Assembly.ReflectionOnlyLoad (String) :

The reflection-only context is no different from other contexts. Assemblies that are loaded into the context can be unloaded only by unloading the application domain.

So, I am afraid the only way to unload an assembly is unloading the application domain. To create a new AppDomain and load assemblies into it:

public void TempLoadAssembly()
{
    AppDomain tempDomain = AppDomain.CreateDomain("TemporaryAppDomain");
    tempDomain.DoCallBack(LoaderCallback);
    AppDomain.Unload(tempDomain);
}

private void LoaderCallback()
{
    Assembly.ReflectionOnlyLoad("YourAssembly");
    // Do your stuff here
}
Agnes answered 22/10, 2008 at 11:30 Comment(6)
Doesn't the Assembly.RefrectionOnlyLoad(...) still loads the assembly in the Current Domain and not in the TemporaryAppDomain?Inactivate
AZ: No. AppDomain.DoCallback "xecutes the code in another application domain that is identified by the specified delegate" (MSDN). The documentation of Assembly.ReflectionOnlyLoad clearly states that "the assembly is loaded into the reflection-only context of the caller's application domain" (again, MSDN). That means that the assembly is indeed loaded to the temporary app domain.Agnes
Kudos Dr.Jokepu. Just wanted to know... I'm probly doing something wrong on my app. :) thanks.Inactivate
I am trying to do the same thing, and it seems that LoaderCallback has NO access to anything in the original AppDomain. Therefore, no parameters (or members) can be passed between domains. This seems a bit pointless if you can't get the information discovered, back to the calling process.Mccarty
I have the same problem: I need to load an assembly to make a decision on it. If that decision result cannot leave the temporary AppDomain, it's useless. How can I pass data into and back from the other domain?Accentuation
@Neil, the class the method is in is serialized and copied, so it will have access to copies of instance data -- you're best off building a specialized class to do this.Tectonic
A
10

Whilst not really about unloading assemblies, if you're just trying to get the version number of a file you can use System.Diagnostics.FileVersionInfo.

var info = FileVersionInfo.GetVersionInfo(path);

FileVersionInfo has the following properties:

public string Comments { get; }
public string CompanyName { get; }
public int FileBuildPart { get; }
public string FileDescription { get; }
public int FileMajorPart { get; }
public int FileMinorPart { get; }
public string FileName { get; }
public int FilePrivatePart { get; }
public string FileVersion { get; }
public string InternalName { get; }
public bool IsDebug { get; }
public bool IsPatched { get; }
public bool IsPreRelease { get; }
public bool IsPrivateBuild { get; }
public bool IsSpecialBuild { get; }
public string Language { get; }
public string LegalCopyright { get; }
public string LegalTrademarks { get; }
public string OriginalFilename { get; }
public string PrivateBuild { get; }
public int ProductBuildPart { get; }
public int ProductMajorPart { get; }
public int ProductMinorPart { get; }
public string ProductName { get; }
public int ProductPrivatePart { get; }
public string ProductVersion { get; }
public string SpecialBuild { get; }
Aboulia answered 9/4, 2009 at 9:19 Comment(0)
S
10

You can create an instance in the new AppDomain and execute your code in that instance.

var settings = new AppDomainSetup
{
    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
};
var childDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, settings);

 var handle = Activator.CreateInstance(childDomain,
            typeof(ReferenceLoader).Assembly.FullName,
            typeof(ReferenceLoader).FullName,
            false, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, null, CultureInfo.CurrentCulture, new object[0]);


var loader = (ReferenceLoader)handle.Unwrap();

//This operation is executed in the new AppDomain
var paths = loader.LoadReferences(assemblyPath);


AppDomain.Unload(childDomain);

Here is the ReferenceLoader

public class ReferenceLoader : MarshalByRefObject
{
    public string[] LoadReferences(string assemblyPath)
    {
        var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
        var paths = assembly.GetReferencedAssemblies().Select(x => x.FullName).ToArray();
        return paths;
    }
}
Staceestacey answered 22/6, 2016 at 13:48 Comment(1)
Perfect - had to make one minor change to run in LINQPad as follows: 'var settings = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(this.GetType().Assembly.Location) };'Cenesthesia
L
4

You can try to use Unmanaged Metadata API, which is COM and can easily be used from .NET application with some kind of wrapper.

Leahy answered 22/10, 2008 at 11:33 Comment(1)
Hi Ilya. Thanks for this link. I'll be revisiting this work shortly and will definitely try out this API. I'm hoping it's more efficient than actually loading the assembly.Aboulia
B
2

You have to use application domains, there's no other way to unload an assembly. Basically you have to use code like this:


AppDomain tempDomain = AppDomain.CreateDomain("Temp Domain");
tempDomain.Load(assembly);
AppDomain.Unload(tempDomain);

Broadcaster answered 22/10, 2008 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.