MSBuild 16.0 在通过 MSBuildLocator 加载时找不到自己的依赖程序集

问题描述 投票:0回答:2

我正在尝试从 C# DLL(最终将从 PowerShell 加载)以编程方式运行 MSBuild,并作为从命令行应用程序的第一步。我已按照推荐(或者我认为)使用了 Microsoft.Build.Locator,将其 NuGet 包安装到我的项目中,并将以下引用添加到我的测试项目中:

    <Reference Include="Microsoft.Build, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.Build.Locator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9dff12846e04bfbd, processorArchitecture=MSIL">
      <HintPath>..\packages\Microsoft.Build.Locator.1.2.6\lib\net46\Microsoft.Build.Locator.dll</HintPath>
    </Reference>

该项目面向.NET Framework 4.8,源码如下:

using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Locator;
using System.Collections.Generic;

namespace nrm_testing
{
    class Program
    {
        static void Main(string[] args)
        {
            MSBuildLocator.RegisterDefaults();
            DoStuff();
        }
        
        static void DoStuff()
        {
            using (var projectCollection = new ProjectCollection())
            {
                var buildParameters = new BuildParameters
                {
                    MaxNodeCount = 1 // https://stackoverflow.com/q/62658963/3233393
                };
                
                var buildRequestData = new BuildRequestData(
                    @"path\to\a\project.vcxproj",
                    new Dictionary<string, string>(),
                    null,
                    new string[0],
                    null
                );

                var result = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequestData);
            }
        }
    }
}

进入

using
块后,我收到以下异常:

System.IO.FileNotFoundException: 'Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'

模块窗口显示 MSBL 确实成功找到了我的 VS2019 安装:

Microsoft.Build.dll           16.07.0.37604 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Build.dll
Microsoft.Build.Framework.dll 16.07.0.37604 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Build.Framework.dll
Microsoft.Build.Locator.dll   1.02.6.49918  C:\dev\nrm3-tests\nrm\nrm-testing\.out\AnyCPU-Debug\Microsoft.Build.Locator.dll

System.Runtime.CompilerServices.Unsafe.dll
除了已定位的 MSBuild 程序集之外,在版本 4.0.6.0 中确实存在(根据 DotPeek)。

什么可能导致此错误,如何修复它?


到目前为止我的尝试:

  • 我已经找到了这个问题,但是链接的 GitHub 问题仍然处于开放状态,我不确定是否是同样的问题。

  • 我已经设法让绑定重定向工作,但我不认为我可以在 DLL 中使用它们,所以这是一个死胡同。

  • System.Runtime.CompilerServices.Unsafe
    NuGet 包添加到项目中(并验证它确实与项目的可执行文件一起复制)没有任何作用(感谢 magicandre1981 的建议)。

  • 从packages.config切换到PackageReference(按照Perry Qi的建议),行为没有变化。

c# .net visual-studio msbuild visual-studio-2019
2个回答
1
投票

经过大量摆弄不同的想法后,我最终根据手动装配决议编写了这个解决方法。

RegisterMSBuildAssemblyPath
检测
Microsoft.Build.dll
何时加载,并记住其目录。在后续程序集加载失败时,
RedirectMSBuildAssemblies
检查该路径中是否存在丢失的程序集,如果存在则加载它。

class Program
{
    private static string MSBuildAssemblyDir;

    static void Main(string[] args)
    {
        MSBuildLocator.RegisterDefaults();

        Thread.GetDomain().AssemblyLoad += RegisterMSBuildAssemblyPath;
        Thread.GetDomain().AssemblyResolve += RedirectMSBuildAssemblies;

        DoStuff();
    }

    private static void RegisterMSBuildAssemblyPath(object sender, AssemblyLoadEventArgs args)
    {
        var assemblyPath = args.LoadedAssembly.Location;

        if (Path.GetFileName(assemblyPath) == "Microsoft.Build.dll")
            MSBuildAssemblyDir = Path.GetDirectoryName(assemblyPath);
    }

    private static Assembly RedirectMSBuildAssemblies(object sender, ResolveEventArgs args)
    {
        if (MSBuildAssemblyDir == null)
            return null;

        try
        {
            var assemblyFilename = $"{args.Name.Split(',')[0]}.dll";
            var potentialAssemblyPath = Path.Combine(MSBuildAssemblyDir, assemblyFilename);

            return Assembly.LoadFrom(potentialAssemblyPath);
        }
        catch (Exception)
        {
            return null;
        }
    }

    static void DoStuff()
    {
        // Same as before
    }
}

我很确定有(很多)极端情况会导致此失败,但现在就可以了。


0
投票

实际上,这是packages.config nuget管理格式长期以来的一个现实问题。 微软针对此问题推荐的解决方案是添加一个绑定重定向。

通常,您可以在

xxx.csproj
文件中使用此节点来自动生成绑定重定向。

但是,对于某些特定的dll,该节点可能会由于多种原因而无法工作。正如您所说,这仍然是当前

packages.config nuget management format
的一个问题。

建议

作为建议,自 VS2017 起,您可以使用 新的 PackageReference nuget 管理格式。这种格式简单、方便、高效。

另外,当您使用这种格式时,首先应该备份您的项目。

只需右键单击

packages.config
文件-->单击
Migrate packages.config to PackageReference

此外,我也在DC论坛上报告了这个问题,希望团队能提供更好的建议。

© www.soinside.com 2019 - 2024. All rights reserved.