【问题标题】:How to force loading of an referenced assembly that is not explicitly used by the project如何强制加载项目未明确使用的引用程序集
【发布时间】:2017-11-22 12:39:23
【问题描述】:

我有以下场景:

一个宿主应用程序,它宿主(win)在同一解决方案的其他项目中定义的表单。托管的表单都实现了相同的接口。由于宿主应用程序引用了所有其他项目,因此不涉及(也不需要)动态加载(程序集解析)。

现在我认为我可以迭代AppDomain.CurrentDomain.GetAssemblies(),获取所有类型并过滤哪些类型实现了我的接口。但由于我从未明确使用主机项目中的其他项目,因此程序集不会加载到主机应用程序中,因此AppDomain.CurrentDomain.GetAssemblies() 不会列出它们。

我认为在大多数情况下这种行为是好的,但在我的情况下(反射使用)这是一个问题。有没有办法强制加载被引用但未明确使用的(特定)程序集?我正在寻找视觉工作室/项目设置的方法来做到这一点 - 我不喜欢编写代码来加载已经引用的程序集的想法:(

可能的解决方案:

  • 为每个程序集致电Assembly.Load。缺点:主机应用程序中的代码更改
  • 从表单项目中调用已知的类/方法来强制加载。缺点:主机应用程序中的代码更改以及要调用的类/方法的知识

【问题讨论】:

  • 所以你想在不写任何代码的情况下做到这一点,对吧?
  • @Evk 我不想在每次添加新项目时添加代码。如果代码能够处理新添加的项目引用(忽略其他未引用的 DLL)而无需修改,那就没问题了。
  • 那个答案是 7 岁,请注意。现在可能有更好的选择。
  • 我不确定这是否是您想要的,但您可以使用 T4 模板枚举解决方案中的所有项目及其引用的程序集,并输出包含信息的代码文件。跨度>

标签: c# .net


【解决方案1】:

根据 TnTinMn 的评论回答。感谢您的建议!

  • 将静态Program 类(宿主应用的入口点)更改为partial
  • 创建一个名为 Program.AssemblyLoader.tt 的 TextTemplate/T4 文件
  • 添加以下内容:

Program.AssemblyLoader.tt:

<#@ template language="C#" hostSpecific="true" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="EnvDte" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System" #>
<#

    var visualStudio = (EnvDTE.DTE)(Host as IServiceProvider).GetService(typeof(EnvDTE.DTE));
    var solution = (EnvDTE.Solution)visualStudio.Solution;
    var projects = solution.Projects;

    // Assume that the first item is the main project, the host app.
    // Index is **NOT** zero-based!
    var main = (EnvDTE.Project)projects.Item(1);
    var p = main.Properties.Item("DefaultNamespace");
    var ns = p.Value.ToString();

#>
using System;
using System.Linq;
using System.IO;
using System.Reflection;

namespace <# WriteLine(ns); #>
{
    static partial class Program
    {
        static Program()
        {
            //! Generated file. Do not edit. Check the .tt file instead!
<#
foreach(EnvDTE.Project proj in projects)
{
    if(proj.Properties == null || proj.Properties.Item("OutputFileName") == null)
        continue;

    EnvDTE.Property p2 = proj.Properties.Item("OutputFileName") as EnvDTE.Property;
    WriteLine("            LoadAssemblyIfNecessary(\"" + p2.Value + "\");");
}
#>
        }

        private static readonly AssemblyName[] REFS = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
        private static readonly string APP = Path.GetFileName(System.Windows.Forms.Application.ExecutablePath);

        private static void LoadAssemblyIfNecessary(string name)
        {
            if (string.Equals(name, APP, StringComparison.InvariantCultureIgnoreCase)) return;

            if (!REFS.Any(x => x.Name.StartsWith(Path.GetFileNameWithoutExtension(name), StringComparison.InvariantCultureIgnoreCase)))
            {
                AppDomain.CurrentDomain.Load(Path.GetFileNameWithoutExtension(name));
            }
        }
    }
}

生成的Program.AssemblyLoader.cs示例:

using System;
using System.Linq;
using System.IO;
using System.Reflection;

namespace Your.Name.Space
{
    static partial class Program
    {
        static Program()
        {
            //! Generated file. Do not edit. Check the .tt file instead!
            LoadAssemblyIfNecessary("HostApp.dll");
            LoadAssemblyIfNecessary("Dependency1.dll");
            LoadAssemblyIfNecessary("Dependency2.dll");
            LoadAssemblyIfNecessary("Modul1.exe");
        }

        private static readonly AssemblyName[] REFS = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
        private static readonly string APP = Path.GetFileName(System.Windows.Forms.Application.ExecutablePath);

        private static void LoadAssemblyIfNecessary(string name)
        {
            if (string.Equals(name, APP, StringComparison.InvariantCultureIgnoreCase)) return;

            if (!REFS.Any(x => x.Name.StartsWith(Path.GetFileNameWithoutExtension(name), StringComparison.InvariantCultureIgnoreCase)))
            {
                AppDomain.CurrentDomain.Load(Path.GetFileNameWithoutExtension(name));
            }
        }
    }
}

【讨论】:

  • +1 我很高兴看到有人可以发表评论并研究/实施它以满足他们的需求。在给我一个C&P的答案的这些日子里,这是一种难得而美妙的体验。
  • 但在此示例中,用户必须创建自己的入口点,即 Program 类,部分。这是用户必须满足的要求。在组装是第三方并且用户只是消费者的情况下,他必须使这个显式。这让我烦了很久。我得出的结论是,您可以将模块初始化程序注入到客户的项目组装输出中。在 c# 9 中,只需添加一个由 [module: ModuleInitializer(typeof(ForeignAssembly.MyModuleInitializer))] 装饰的 c# 文件就应该更容易一站了。
猜你喜欢
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多