程序集反射加载和应用程序域冲突

Posted

技术标签:

【中文标题】程序集反射加载和应用程序域冲突【英文标题】:Assembly reflection loading and app domain conflict 【发布时间】:2017-04-04 17:59:14 【问题描述】:

我正在尝试制作一个小型 C# 工具来比较两个 svn 修订版本并跟踪任何类中的属性更改。我的目标是在不使用 Momo.Cecil 的情况下使用反射来比较我的 dll 的每个类的属性。

通过实验,然后阅读这篇文章 Assembly Loading 和在 Google 上找到的一些线程,我了解到如果我们不使用 Assembly.ReflectionOnlyLoadFrom,两个具有相同身份的 DLL 将被解析为相同。

尝试使用他们的代码为每次加载创建一个 AppDomain,并尝试从搜索中获得许多变体,我得到了这个异常,我找不到任何线程来解释如何解决这个问题:

API 限制:程序集 'file:///D:\somepath\82\MyLib.dll' 已从其他位置加载。无法从同一应用域中的新位置加载。

此错误发生在以下行的第二次调用(路径 82)上:

Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);

也许我不了解一些非常基本的东西,这使我无法正确创建新的 AppDomain?

这是用于重现此问题的所有代码。

我的入口点的代码

        //Let say one of the property has been renamed between both commits
        var svnRev81Assembly = ReflectionOnlyLoadFrom(@"D:\somepath\81\MyLib.dll");
        var svnRev82Assembly = ReflectionOnlyLoadFrom(@"D:\somepath\82\MyLib.dll");

加载器的实现

    private string _CurrentAssemblyKey;
    private Assembly ReflectionOnlyLoadFrom(string assemblyPath)
    
        var path = Path.GetDirectoryName(assemblyPath);

        // Create application domain setup information.
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = path;
        domaininfo.PrivateBinPath = path;

        _CurrentAssemblyKey = Guid.NewGuid().ToString();
        AppDomain currentAd = AppDomain.CreateDomain(_CurrentAssemblyKey, null, domaininfo); //Everytime we create a new domain with a new name        
        //currentAd.ReflectionOnlyAssemblyResolve += CustomReflectionOnlyResolver; This doesnt work anymore since I added AppDomainSetup        
        currentAd.SetData(_CurrentAssemblyKey, path);

        //Loading to specific location -  folder 81 or 82
        Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);

        //Preloading the 
        foreach (var assemblyName in assembly.GetReferencedAssemblies())
        
            Assembly.ReflectionOnlyLoadFrom(Path.Combine(path, assemblyName.Name + ".dll"));
        
        Type[] types = assembly.GetTypes();

        // Lastly, reset the ALS entry and remove our handler
        currentAd.SetData(_CurrentAssemblyKey, null);
        //currentAd.ReflectionOnlyAssemblyResolve -= CustomReflectionOnlyResolver; This doesnt work anymore since I added AppDomainSetup  

        return assembly;
    

【问题讨论】:

【参考方案1】:

这可以通过在单独的应用程序域中加载 dll 来解决。

private static void ReflectionOnlyLoadFrom()

    var appDomain = AppDomain.CreateDomain("Temporary");
    var loader1 = (Loader)appDomain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
    loader1.Execute(@"D:\somepath\81\MyLib.dll");
    var loader2 = (Loader)appDomain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
    loader2.Execute(@"D:\somepath\82\MyLib.dll");
    loader1.CompareTwoDLLs(loader2);

    AppDomain.Unload(appDomain);

Loader.cs

public class Loader : MarshalByRefObject

    public Assembly TempAssembly  get; set; 
    public string Execute(string dllPath)
    
        TempAssembly = Assembly.LoadFile(dllPath);
        return TempAssembly.FullName;
    
    public void GetRefAssemblyTypes()
    
        foreach (var refAssembly in TempAssembly.GetReferencedAssemblies())
        
            var asm = Assembly.Load(refAssembly);
            var asmTypes = asm.GetTypes();
        
    
    public void CompareTwoDLLs(Loader l2)
    
        var types1 = TempAssembly.GetTypes();
        var types2= l2.TempAssembly.GetTypes();
        GetRefAssemblyTypes();
        //logic to return comparison result
    
 

【讨论】:

嗨,我玩过你的解决方案,它确实适用于一些基本的 dll。一旦 dll 有依赖关系,它就没有。我确实尝试在 Execute 方法中为 TempAssembly.GetReferenceAssemblies() 添加 Assembly.LoadFile,但它给出“无法加载文件或程序集或其依赖项之一。系统找不到指定的文件。”尝试执行 GetTypes() 时。 使用 Loader,您可以跨应用程序域边界进行通信。我可以通过在加载器类中添加这个方法来获取引用的程序集。使用 GetRefAssemblyTypes 方法编辑了我的答案。

以上是关于程序集反射加载和应用程序域冲突的主要内容,如果未能解决你的问题,请参考以下文章

通过应用程序域AppDomain加载和卸载程序集

C# 动态加载程序集信息

第二十三章 程序集加载和反射

程序集加载和反射

程序集,元数据,应用程序域,反射,特定属性

C#使用反射加载多个程序集