从Nuget Package中自动提取本地和托管的DLL文件

53
这几个月来一直困扰着我,但我仍然无法实现它。 我的托管库是从Nuget包中提取出来的,但本地库却没有。
我们有一堆由另一家公司提供的托管库和本地库。我们同时拥有x86和x64版本。为了在ASP.NET Core项目中使用它们,我必须创建一个Nuget包。
我的架构如下:
- 一个ASP.NET Core类库,我将其更改为仅针对完整的.NET Framework。此项目引用了我的Nuget包。 - 一个ASP.NET Core网站,也针对完整的.NET Framework,并引用类库。
当然,最终我需要将本地库提取到网站的正确运行时文件夹中(例如:\bin\Debug\net461\win7-x64)。
目前我的解决方案是:
  • 将本地库放在build文件夹中
  • 创建一个targets文件,将它们复制到$(OutputPath)(甚至不是运行时文件夹)
  • 在我的网站的xproj中添加一些MsBuild命令,以获取目标文件在$(USERPROFILE)\.nuget\packages\文件夹中并执行它
  • 现在手动将从bin文件夹中提取的本机DLL复制到runtime

我曾尝试直接使用project.json中的一些配置将它们直接复制到运行时文件夹中(我真的不记得我为这部分尝试了所有的事情),但这总是失败的。此外,即使我在我的目标文件中指定了SkipUnchangedFiles="true",这也被忽略了,我的DLL在每次构建时都会被复制到我的bin文件夹中。

这是一个繁重的过程,只为实现DLL提取,现在我真的想摆脱所有的MsBuild,并获得一个更简单的解决方案。

我知道在新版Nuget中,它现在能够原生地提取它们,无需添加自定义的MsBuild命令。如此处所述,C#项目甚至不需要targets文件。

接下来,可能会使用您的NuGet软件包的C++和JavaScript项目需要一个.targets文件来识别必要的程序集和winmd文件。(C#和Visual Basic项目会自动完成此操作。)

我在浏览器中保留了一个选项卡几个月(原始链接),并意识到这个资源最近已从Nuget网站中删除。它解释了如何使用runtimes文件夹来自动提取本机DLL。然而,我从未能够按照其所述获得成功结果。现在,这个页面已被删除,并由这个页面替换,其中解释很少,根本不涉及runtimes文件夹。
我猜应该使用runtimes文件夹来存放本机DLL,而使用lib文件夹来存放托管代码,但我不能百分之百确定。(那么我应该使用build文件夹吗?)
我尝试了几种方法(我记不清尝试了多少次,就像我说的,头痛了几个月...),比如这个架构(我不明白为什么要在runtimes下有build/native和natives文件夹) enter image description here 我还尝试使用.NET框架版本结构,就像这里描述的一样,用于我的托管库。 这个似乎也是解决方案的一部分。
编译器在创建程序集引用时会忽略架构,这是一个加载时间的概念。如果存在特定架构的引用,加载器将优先选择它。您可以使用corflags从x86程序集中删除架构来生成AnyCPU程序集,例如:corflags /32BITREQ- MySDK.dll。Corflags是.NET SDK的一部分,可以在VS的开发人员命令提示符中找到。
我已经将x86和x64 DLL都转换为AnyCPU(不知道是否对x64 DLL有影响,但我没有收到错误信息),然后尝试了Nuget包中的多种不同架构,但仍然无法正常工作。默认的运行时没有在project.json中指定任何条目,是win7-x64,所以我决定明确指定它以防万一。
"runtimes": {
    "win7-x64": {}
},

这是我在Nuget包中使用的运行时标识符,但我不关心Windows版本。实际上,我更喜欢win-x86或win-x64,但根据this page,它似乎是无效的值。

Windows RIDs

Windows 7 / Windows Server 2008 R2

  • win7-x64
  • win7-x86

Windows 8 / Windows Server 2012

  • win8-x64
  • win8-x86
  • win8-arm

Windows 8.1 / Windows Server 2012 R2

  • win81-x64
  • win81-x86
  • win81-arm

Windows 10 / Windows Server 2016

  • win10-x64
  • win10-x86
  • win10-arm
  • win10-arm64

然而,Github源代码中描述了更多的RID,所以哪个源是正确的?

正如您所看到的,这里有很多的谜团,主要是因为缺乏文档或不同文档之间的矛盾。

如果我至少能有一个工作示例,那么我就可以执行我的测试来回答其他问题,比如尝试使用通用的win-x64 RID,或者查看是否可以包含我的托管库,无论.NET Framework版本如何。

请注意我的特殊上下文:我有一个针对完整.NET Framework的ASP.NET Core项目

感谢您的答案,我渴望让这个简单的东西工作。


你必须使用NuGet包吗?我认为你不一定需要使用它们,因为这是一个ASP.NET Core项目。我很确定你可以通过添加引用 > 浏览并选择DLL的方式来实现。 - SimonGates
不,我不能直接添加本地DLL的引用,必须创建一个包。因此,我想将它们全部(本地和托管)打包到一个Nuget包中。 - Jérôme MEVEL
也许看一下这个问题在 github.com/aspnet/dnx/issues/402#issuecomment-151040736 这里。似乎已经添加了在 RC1 中加载它的方法。 - Tseng
特别是 @Pawel 的这篇博客文章 http://blog.3d-logic.com/2015/11/10/using-native-libraries-in-asp-net-5/,因为它总结了这个问题。根据 David Fowl 的说法,在 RC2 及以后的版本中应该可以工作。 - Tseng
是的,我现在知道这是可能的,我已经在Twitter上和Jon Skeet进行了简短的讨论。我不明白我做错了什么,因为我遵循$/runtimes/{runtime-id}/native结构。 - Jérôme MEVEL
你看过 https://github.com/Mizux/dotnet-native 吗? - Mizux
3个回答

67

我会尽可能详细地解释我经历的所有痛苦和解决方案。在我的示例中,我使用简单的文本文件 AAA86.txt AAA64.txt AAAany.txt 代替原生DLL,以简单演示提取过程。

你需要知道的第一件事: 如果您尝试将 native NuGet的架构与包含某些托管库的 lib 文件夹混合使用,则它将无法正常工作

enter image description here

在这种情况下,您的托管DLL将被复制到项目的输出目录,但不会复制原生DLL。

感谢Jon Skeet指引我朝着正确的方向前进,并建议我查看Grpc.Core包。诀窍是创建一个 targets 文件来处理DLL提取。

enter image description here

您的targets文件应包含像这样的内容

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <ItemGroup Condition=" '$(Platform)' == 'x64' ">
        <Content Include="$(MSBuildThisFileDirectory)..\runtimes\win-x64\native\AAA64.txt">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            <Link>AAA64.txt</Link>
        </Content>
    </ItemGroup>

    <ItemGroup Condition=" '$(Platform)' == 'x86' OR '$(Platform)' == 'AnyCPU' ">
        <Content Include="$(MSBuildThisFileDirectory)..\runtimes\win-x86\native\AAA86.txt">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            <Link>AAA86.txt</Link>
        </Content>
    </ItemGroup>

</Project>

同时确保你的.targets文件与你的程序集名称相同。例如,如果你的程序集名称是DemoPackage,你的targets文件应该命名为DemoPackage.targets。否则,在另一个项目中引用包时,.targets文件可能不会被应用。

enter image description here

现在还有几件事情需要注意:

1) Visual Studio完全不关心你选择的设置,它总是使用虚拟RID。(在我的情况下,即使我在Windows 10上,我最终总是得到一个win7-x64文件夹...)

enter image description here

2) 在你的project.json中,platform设置也完全无用。

{
    "buildOptions": {
        "platform": "x64"
    }
}

3)运行时设置中,如果您仅设置win和/或win-x64

"runtimes": {
    "win": {},
    "win-x64": {}
}

Visual Studio将使用win7-x64,但如果您在Windows 10计算机上添加了win10-x64,则会使用该RID。

4) 如果您使用类似于此的通用RID编译应用程序

dotnet build -c debug -r win

然后您的targets文件将收到您机器的架构信息(在我的情况下是x64),而不是我原本期望的AnyCPU

5) 只使用本地库(没有任何托管库)时,如果遵循架构runtimes/RID/native,提取将在没有目标文件的情况下工作

6) 如果我的包中只有本地库,则所选的RID将始终为win-x64,因为我告诉过您运行时文件夹始终会创建为win7-x64,无论我选择哪种配置。 如果我的包中只有一个win RID,那么它将成功被选择。

编辑:

最后一个有用的提示是,在执行您的.targets文件的当前目录中打印出当前目录可能会很方便

<Target Name="TestMessage" AfterTargets="Build" >
    <Message Text="***********************************************************" Importance="high"/>
    <Message Text="$(MSBuildThisFileDirectory)" Importance="high"/>
    <Message Text="***********************************************************" Importance="high"/>
</Target>

在 Visual Studio 的 Build 输出中,将打印出您的目录。


2
如果我能加10分,我会这么做的,非常好的答案。 - Mansoor
1
不确定,但我认为路径必须是Include="$(MSBuildThisFileDirectory)..\runtimes\(只有一个..\),因为当前在您的帖子中,它尝试在特定包的所有NuGet版本文件夹所在的目录中查找运行时。 - Dimmerworld
@Dimmerworld,考虑到我花了大量的时间来完成这项任务,我觉得自己犯这种错误是很奇怪的。不确定这是我的问题还是自2016年以来有什么改变,无论如何,我已经更正了我的回答。谢谢。 - Jérôme MEVEL

2
作为对@Jérôme MEVEL答案的延续,GeneratePathProperty是一项新功能,可在NuGet 5.0或更高版本和Visual Studio 2019 16.0或更高版本中使用。使用此功能,您可以了解包在包的使用者中安装的确切路径。如果包名为Mypackage.ID,则可以将其路径称为:$(PkgMypackage_id),并将点'.'替换为下划线'_'。在包使用者中,向PackageReference添加GeneratePathProperty="true"属性即可。
  <ItemGroup>
      <PackageReference Include="Some.Package" Version="1.0.0" GeneratePathProperty="true" />
  </ItemGroup>

在软件包创建器中:

软件包路径可以通过以下方式访问:

<ItemGroup Condition=" '$(Platform)' == 'x64' ">
        <Content Include="$(PkgMyPackage_Id)\runtimes\win-x64\native\AAA64.txt">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            <Link>AAA64.txt</Link>
        </Content>
    </ItemGroup>

请注意路径为:"$(PkgMyPackage_Id)\runtimes\win-x64\native\AAA64.txt

如需更多详细信息,请阅读:GeneratePathProperty


0
现在使用 dotnet restore 而不是 nuget.exe restore,本地 DLL 未被复制到输出目录的问题已经解决。

当我使用特定的 dotnet restore --runtime win-x64 <path_to_the_solution.sln>时,我的问题得到了解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接