【问题标题】:How to load a .NET assembly for reflection operations and subsequently unload it?如何为反射操作加载 .NET 程序集并随后将其卸载?
【发布时间】:2010-09-18 12:39:51
【问题描述】:

我正在编写一个工具来报告有关在我的客户系统内跨环境和区域部署的 .NET 应用程序的信息。

我想读取这些程序集中的程序集属性值。

这可以使用Assembly.ReflectionOnlyLoad 来实现,但是即使这种方法也会保持程序集的加载。这里的问题是我无法从不同的路径加载两个同名的程序集,所以自然无法比较部署在不同系统中的同一个应用程序。

此时我假设解决方案将涉及使用临时AppDomains。

谁能详细说明如何将程序集加载到另一个AppDomain,从中读取属性然后卸载AppDomain

这需要适用于文件系统上的程序集以及 URL 地址上的程序集。

【问题讨论】:

    标签: .net assemblies appdomain assembly-attributes


    【解决方案1】:

    来自MSDN documentation of System.Reflection.Assembly.ReflectionOnlyLoad (String)

    仅反射上下文是 no 不同于其他语境。 加载到 上下文只能通过 卸载应用程序域。

    因此,恐怕卸载程序集的唯一方法是卸载应用程序域。 创建一个新的 AppDomain 并将程序集加载到其中:

    public void TempLoadAssembly()
    {
        AppDomain tempDomain = AppDomain.CreateDomain("TemporaryAppDomain");
        tempDomain.DoCallBack(LoaderCallback);
        AppDomain.Unload(tempDomain);
    }
    
    private void LoaderCallback()
    {
        Assembly.ReflectionOnlyLoad("YourAssembly");
        // Do your stuff here
    }
    

    【讨论】:

    • 谢谢,这看起来很有用。
    • Assembly.RefrectionOnlyLoad(...) 是否仍将程序集加载到当前域而不是 TemporaryAppDomain 中?
    • AZ:否。AppDomain.DoCallback“执行由指定委托标识的另一个应用程序域中的代码”(MSDN)。 Assembly.ReflectionOnlyLoad 的文档清楚地指出“程序集被加载到调用者应用程序域的仅反射上下文中”(同样,MSDN)。这意味着程序集确实已加载到临时应用程序域。
    • 向 Jokepu 博士致敬。只是想知道......我可能在我的应用程序上做错了什么。 :) 谢谢。
    • 我正在尝试做同样的事情,似乎 LoaderCallback 无法访问原始 AppDomain 中的任何内容。因此,域之间不能传递任何参数(或成员)。如果您无法将发现的信息返回到调用过程中,这似乎有点毫无意义。
    【解决方案2】:

    虽然不是真正关于卸载程序集,但如果您只是想获取文件的版本号,您可以使用System.Diagnostics.FileVersionInfo

    var info = FileVersionInfo.GetVersionInfo(path);
    

    FileVersionInfo 具有以下属性:

    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; }
    

    【讨论】:

      【解决方案3】:

      您可以在新的 AppDomain 中创建一个实例并在该实例中执行您的代码。

      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);
      

      这里是参考加载器

      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;
          }
      }
      

      【讨论】:

      • 完美 - 必须做一个小改动才能在 LINQPad 中运行,如下所示:'var settings = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(this.GetType().Assembly.Location) };'
      【解决方案4】:

      您可以尝试使用Unmanaged Metadata API,它是 COM,可以通过某种包装器轻松地从 .NET 应用程序中使用。

      【讨论】:

      • 嗨伊利亚。感谢您提供此链接。我很快会重新审视这项工作,并且一定会尝试这个 API。我希望它比实际加载程序集更有效。
      【解决方案5】:

      您必须使用应用程序域,没有其他方法可以卸载程序集。基本上你必须使用这样的代码:

      AppDomain tempDomain = AppDomain.CreateDomain("临时域"); tempDomain.Load(程序集); AppDomain.Unload(tempDomain);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-08
        • 2012-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-16
        相关资源
        最近更新 更多