dotnet build和msbuild的区别

问题

最近在开发vsto的excel插件,在vs里可以正常编译,但使用dotnet build会报下面这个错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MSBuild version 17.4.0+18d5aef85 for .NET
正在确定要还原的项目…
所有项目均是最新的,无法还原。
F:\ExcelAddin.csproj(333,3): error MSB4019: 找不到导入的项目“C:\Program Files\dotnet\sdk\7.0.100\Microsoft\VisualStudio\v17.0\OfficeTools\Microsoft.
VisualStudio.Tools.Office.targets”。请确认 Import 声明“C:\Program Files\dotnet\sdk\7.0.100\Microsoft\VisualStudio\v17.0\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets”中的表达式正确,且文件位于磁盘上 。

生成失败。

F:\ExcelAddin.csproj(333,3): error MSB4019: 找不到导入的项目“C:\Program Files\dotnet\sdk\7.0.100\Microsoft\VisualStudio\v17.0\OfficeTools\Microsoft.
VisualStudio.Tools.Office.targets”。请确认 Import 声明“C:\Program Files\dotnet\sdk\7.0.100\Microsoft\VisualStudio\v17.0\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets”中的表达式正确,且文件位于磁盘上 。
0 个警告
1 个错误

已用时间 00:00:00.77

猜想

检查了下csproj,发现这么一段:

1
2
<!-- Include additional build rules for an Office application add-in. -->
<Import Project="$(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets" Condition="'$(VSToolsPath)' != ''" />

VSToolsPath在csproj上面有定义:

1
2
3
4
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

那么问题很明显了,在dotnet build和msbuild的不同环境下,MSBuildExtensionsPath32变量的值不一样。
我们可以使用Message输出变量来验证。

验证

在csproj的Project根节点下添加一个Target

1
2
3
4
5
<Project ToolsVersion="16.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="PrintLog">
<Message Text='MSBuildExtensionsPath="$(MSBuildExtensionsPath)"' Importance="high" />
<Message Text='MSBuildExtensionsPath32="$(MSBuildExtensionsPath32)"' Importance="high" />
</Target>

启动Visual Studio Developer Powershell,使用msbuild运行这个Target

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS > msbuild ExcelAddin.csproj /t:PrintLog
MSBuild version 17.4.0+18d5aef85 for .NET Framework
生成启动时间为 2022/12/9 15:50:26
项目“F:\ExcelAddin.csproj”在节点 1 上(PrintLog 个目标)。
PrintLog:
MSBuildExtensionsPath="C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild"
MSBuildExtensionsPath32="C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild"
已完成生成项目“F:\ExcelAddin.csproj”(PrintLog 个目标)的操作。


已成功生成。
0 个警告
0 个错误

已用时间 00:00:00.23

再使用dotnet build验证一遍。
但要注意,需要先将csproj修复,最简单的办法是先注释掉Import Project="$(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets"
顺带一提,dotnet build等效于dotnet msbuild --restore

1
2
3
4
PS > dotnet msbuild --target:PrintLog
MSBuild version 17.4.0+18d5aef85 for .NET
MSBuildExtensionsPath="C:\Program Files\dotnet\sdk\7.0.100\"
MSBuildExtensionsPath32="C:\Program Files\dotnet\sdk\7.0.100"

验证了我们的猜测,在dotnet build和msbuild的不同环境下,属性是不相同的。

修改环境变量

我们试试强行修改dotnet build的环境变量。

1
PS > dotnet msbuild -property:MSBuildExtensionsPath32="C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild"

仍然报错,错误信息比较多,这里只列出第一个:

1
2
3
C:\Program Files\dotnet\sdk\7.0.100\Microsoft.Common.CurrentVersion.targets(2352,5): warning MSB3245: 未能解析此引用。未能找到程序集“Microsoft.Office.Tools.v4.0.Framework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b
03f5f7f11d50a3a, processorArchitecture=MSIL”。请检查磁盘上是否存在该程序集。 如果您的代码需要此引用,则可能出现编译错误。
[F:\ExcelAddin.csproj]

很明显这个错误是Microsoft.Office.Tools等依赖带来的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ItemGroup>
<Reference Include="Microsoft.Office.Tools.v4.0.Framework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.Tools.Applications.Runtime, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Office.Tools, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Office.Tools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Office.Tools.Excel, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>

在Visual Studio环境下,这些包都内置了。而在dotnet build环境下,找不到这些包。

总结

开发vsto这种特异性的应用没办法使用通用的dotnet工具,老老实实的用msbuild吧。这里有另一个例子

另外,如果想使用vscode写代码,也需要特别配置一下C#插件:

1
2
3
{
"omnisharp.useModernNet": false
}

否则OmniSharp server会使用dotnet环境,导致无法正常工作。