我有一个解决方案与2个项目:MSBuildQuestion.csprojNativeLibrary.vcxproj.

MSBuildQuestion.csproj等于MSBuildQuestion.exe.NativeLibrary.vcxproj等于NativeLibrary.dll.

MSBuildQuestion.csproj通过ProjectReference引用NativeLibrary.vcxproj,还通过CopyToOutputDirectory:

<ItemGroup>
    <ProjectReference Include="..\NativeLibrary\NativeLibrary.vcxproj" />
    <Content Include="..\$(Platform)\$(Configuration)\*.dll">
        <Link>NativeLibrary.dll</Link>
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
</ItemGroup>

当我清理(通过删除所有输出文件夹),然后通过MSBuild从命令行构建解决方案时,它不会将NativeLibrary.dll复制到MSBuildQuestion.csproj的输出,尽管CopyToOutputDirectory明确地告诉它这样做.当我try 运行MSBuildQuestion.exe时,这当然会导致"Unable to Load Dll‘NativeLibrary.dll’"错误.

我是不是遗漏了什么,而这不是本机DLL在*.csproj中应该被引用的方式?还是说这是MSBuild中的一个错误?

以下是我清理、构建和运行我的解决方案的方式:

Get-ChildItem . -include bin,obj,x64 -Recurse | ForEach-Object ($_) { Remove-Item $_.FullName -Force -Recurse }
nuget restore
msbuild /p:Platform=x64 /p:Configuration=Release
.\MSBuildQuestion\bin\x64\Release\net6.0\MSBuildQuestion.exe

重现这个问题的项目是here.


有一个观察:如果我运行msbuild /p:Platform=x64 /p:Configuration=Release两次,NativeLibrary.dll被复制了,然后一切都正常了.好像MSBuild违反了必须生成项目的顺序.不过,如果我判断MSBuild日志(log),似乎并没有违反顺序,因为接下来的行是按预期顺序记录的:

Done Building Project "...\NativeLibrary.vcxproj"
Done Building Project "...\MSBuildQuestion.csproj"
Done Building Project "...\MSBuildQuestion.sln"

推荐答案

NativeLibrary.dll is NOT a Dependency

托管代码是编写到.NET CLR(.NET或.NET框架实现)的代码.托管代码项目可以具有对其他托管代码程序集的编译依赖项.但是本机代码程序集不能是编译时依赖项.托管代码调用本机代码的互操作性在运行时实现.

一个C#项目可以有ProjectReference个项目.ProjectReference解析为编译依赖项程序集.本机程序集不能是编译依赖项.NativeLibrary.dll不是你的C#项目的依赖项,不是直接的,也不是可传递的.

NativeLibrary.dll是代码中DllImport属性的依赖项,但这是在运行时.并且构建对运行时依赖项一无所知.

构建顺序

项目的生成顺序由项目间的ProjectReference个项和解决方案文件中设置的Project Dependencies个项确定.

在解决方案中设置项目依赖项只会影响生成顺序.它没有做一个引用或一个‘依赖’.

MSBuild阶段

MSBuild在处理项目时有两个阶段:evaluation and execution.在判断阶段,"顶层"项目组得到解决.

MSBuildQuestion.sln示例

MSBuildQuestion.csproj包含:

<Project Sdk="Microsoft.NET.Sdk">

    ...

    <ItemGroup>
        <ProjectReference Include="..\NativeLibrary\NativeLibrary.vcxproj" />
        <Content Include="..\$(Platform)\$(Configuration)\*.dll">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
    </ItemGroup>

</Project>

在判断阶段,MSBuild将同时查看和处理ProjectReferenceContent.

ProjectReferenceNativeLibrary.vcxproj会影响构建顺序.MSBuild将在MSBuildQuestion.csproj的执行阶段之前知道构建NativeLibrary.vcxproj.因为它是本机项目,所以没有可用来编译的托管程序集.

将对Content进行判断,除非有来自以前版本的文件挂起,否则将不匹配任何文件.

在执行阶段,有一个复制添加到Content的文件的步骤.除非在判断阶段之前的构建留下了满足通配符的文件,否则NativeLibrary.dll不会在Content中.

(顺便说一句,如果您想要增量构建,请使用PreserveNewest而不是Always.)

要使此示例起作用,请执行以下操作:

  • MSBuildQuestion.csproj中减go <ProjectReference Include="..\NativeLibrary\NativeLibrary.vcxproj" />.
  • 修改解决方案文件,使MSBuildQuestion.csprojNativeLibrary.vcxproj具有项目依赖关系.

该解决方案将在MSBuildQuestion.csproj之前构建NativeLibrary.vcxproj,以确保MSBuildQuestion.csproj的判断阶段将看到NativeLibrary.vcxproj的当前构建文件.NativeLibrary.dll将成为Content的一部分,并将作为Content的一部分被复制.

MSBuildQuestion2.sln示例

在本例中:

  • MSBuildQuestion2.csproj生成依赖于NativeLibraryWrapper.dll的.exe程序集.
  • NativeLibraryWrapper.csproj生成DllImportNativeLibrary.dll的.dll程序集.
  • NativeLibrary.vcxproj构建本机DLLNativeLibrary.dll.

当项目引用解析为对托管程序集的引用时,将复制可传递的托管依赖项.与要复制的程序集的名称匹配的文件(例如.pdb文件)将被复制.

NativeLibrary.dll美元和这些都不一样.

这可以像前面的例子一样解决:

  • 修改解决方案文件以设置MSBuildQuestion2.csproj依赖于NativeLibrary.vcxproj的项目依赖项.
  • NativeLibrary.dllContent.

是的,这不是干的,很容易忘记.

另一种做法是让NativeLibraryWrapper.csproj发布一个文件夹或一个包.用托管代码编写的使用者将使用已发布的文件夹或包,而不是直接引用项目.

.net相关问答推荐

Long.MaxValue从Single到Long的转换导致溢出异常

EF核心类似功能T-SQL UPDATE FROM

.NET发布的应用程序运行与开发不同的端口

升级到.NET8后,SignalR(在坞站容器上)网关损坏

C#:如何构造异步/等待代码,其中许多请求是针对相同的、返回缓慢的数据发出的,这可以满足所有请求

.NET Core 中的微服务

如何正确使用await using语法?

在 C# 中生成随机小数

如何创建 LINQ to SQL 事务?

将 int 转换为 .NET 中的位数组

app.config 文件和 XYZ.settings 文件有什么区别?

使用多个 MemoryCache 实例

Int 到字节数组

在 .NET (C#) 中本地存储数据的最佳方式

为什么使用 ImmutableList 而不是 ReadOnlyCollection?

在 .NET 中计算目录大小的最佳方法是什么?

CryptographicException 未处理:系统找不到指定的文件

无法加载文件或程序集Antlr3.Runtime (1)或其依赖项之一

合并两个(或更多)PDF

嵌套捕获组如何在正则表达式中编号?