我正在尝试从 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的建议),行为没有变化。
经过大量摆弄不同的想法后,我最终根据手动装配决议编写了这个解决方法。
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
}
}
我很确定有(很多)极端情况会导致此失败,但现在就可以了。
实际上,这是packages.config nuget管理格式长期以来的一个现实问题。 微软针对此问题推荐的解决方案是添加一个绑定重定向。
通常,您可以在
xxx.csproj
文件中使用此节点来自动生成绑定重定向。
但是,对于某些特定的dll,该节点可能会由于多种原因而无法工作。正如您所说,这仍然是当前
packages.config nuget management format
的一个问题。
建议
作为建议,自 VS2017 起,您可以使用 新的 PackageReference nuget 管理格式。这种格式简单、方便、高效。
另外,当您使用这种格式时,首先应该备份您的项目。
只需右键单击
packages.config
文件-->单击Migrate packages.config to PackageReference
。
此外,我也在DC论坛上报告了这个问题,希望团队能提供更好的建议。