从 Nuget 包中自动提取本机和托管 DLL
Posted
技术标签:
【中文标题】从 Nuget 包中自动提取本机和托管 DLL【英文标题】:Automatic native and managed DLLs extracting from Nuget Package 【发布时间】:2017-02-27 12:38:02 【问题描述】:这让我疯狂了几个月,但我仍然无法实现它。我的托管库是从 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)
(甚至不是运行时文件夹)
将一些MsBuild命令添加到我网站的xproj
,以获取我的$(USERPROFILE)\.nuget\packages\
文件夹中的目标文件并执行它
复制手动现在在bin
文件夹中提取的本机DLL到runtime
一个
我尝试使用project.json
中的一些配置将它们直接复制到运行时文件夹(老实说,我不记得我为这部分尝试过的所有事情)但这总是失败。此外,即使我在目标文件中指定了SkipUnchangedFiles="true"
,这也会被忽略,并且我的 DLL 在每次构建期间都会复制到我的 bin 文件夹中。
为了实现 DLL 提取,这是一个繁重的过程,现在我真的想摆脱所有 MsBuild 并获得一个更简单的解决方案。
我知道,对于较新版本的 Nuget,它现在能够在本地提取它们,而无需添加自定义 MsBuild 命令的任何帮助。正如here 所写,C# 项目甚至不需要targets
文件
接下来,可能使用 NuGet 包的 C++ 和 javascript 项目需要一个 .targets 文件来识别必要的程序集和 winmd 文件。 (C# 和 Visual Basic 项目会自动执行此操作。)
我在浏览器中打开一个选项卡几个月 (original link) 并意识到该资源最近已从 Nuget 网站上删除。它解释了如何使用runtimes
文件夹自动提取本地DLL。但是,正如所解释的那样,我从未能够获得成功的结果。现在这个页面已被删除并被this one 替换,解释很少,根本不谈论这个runtimes
文件夹。
我的猜测是,我应该将 runtimes
文件夹用于本地 DLL,将 lib
文件夹用于托管,但我不能 100% 确定这一点。 (我也应该使用build
文件夹吗?)
我已经尝试了几件事(我不记得尝试了多少次,因为我说过几个月的头痛......)比如this architecture(我不明白这里有什么build/native
和也是runtimes
下的本地文件夹)
我还尝试将here 所述的 .NET 框架版本结构用于我的托管库。
This 似乎也是解决方案的一部分
在创建程序集引用时,编译器会忽略架构。这是一个加载时间概念。如果存在,加载程序将更喜欢特定于架构的引用。
您可以用来生成 AnyCPU 程序集的一个技巧是使用 corflags 从 x86 程序集中删除体系结构。例如: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 RID
Windows 7 / Windows Server 2008 R2
win7-x64 win7-x86Windows 8 / Windows Server 2012
win8-x64 win8-x86 win8-armWindows 8.1 / Windows Server 2012 R2
win81-x64 win81-x86 win81-armWindows 10 / Windows Server 2016
win10-x64 win10-x86 win10-arm win10-arm64
但是,这个Github source 描述了更多的 RID,那么哪个来源是正确的?
如您所见,这里有很多谜团,主要是因为缺乏文档,甚至不同文档之间存在矛盾。
如果至少我可以有一个工作示例,那么我可以执行我的测试来回答其他问题,例如尝试通用 win-x64
RID,或者看看我是否可以包含一次我的托管库,无论 .NET Framework 版本。
请注意我的特殊上下文:我有一个针对完整 .NET Framework 的 ASP.NET Core 项目
感谢您的回答,我迫切希望让这个简单的事情发挥作用。
【问题讨论】:
你必须使用 NuGet 包吗?我认为您不必使用它们,因为它是 ASP.NET Core 项目。我很确定您可以添加参考 > 浏览并选择一个 DLL? 不,我不能直接添加对本机 DLL 的引用,我必须创建一个包。所以我想将它们全部(原生和托管)打包在一个 Nuget 包中。 也许看这个问题这里github.com/aspnet/dnx/issues/402#issuecomment-151040736 似乎已经添加了在RC1中加载它的方法 尤其是@Pawel 的blog.3d-logic.com/2015/11/10/… 博客文章,因为它有点总结。根据 David Fowl 的说法,它应该在 RC2 及更高版本中工作。 是的,我知道现在有可能,我已经在 Twitter 上与 Jon Skeet 进行了简短的讨论。我不明白我做错了什么我遵循$/runtimes/runtime-id/native
结构
【参考方案1】:
我会尽可能详细地解释我所经历的所有痛苦和解决方案。在我的示例中,我使用简单的文本文件 AAA86.txt
、AAA64.txt
和 AAAany.txt
而不是原生 DLL 来简单地演示提取过程。
您需要知道的第一件事:
如果您尝试将native
NuGet 的体系结构与包含一些托管库的lib
文件夹混合,它将不起作用
在这种情况下,您的托管 DLL 将被复制到项目的输出目录,而不是您的本地目录。
感谢 Jon Skeet,他为我指明了正确的方向,建议我看看 Grpc.Core package。诀窍是创建一个 targets
文件来处理 DLL 提取。
您的目标文件应该包含类似这样的内容
<?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
文件与您的AssemblyName 命名相同。所以如果你的程序集的名字是DemoPackage
,你的目标文件应该命名为DemoPackage.targets
。否则,在另一个项目中引用包时,.targets
文件可能不会被应用。
现在你需要知道的其他一些事情:
1) Visual Studio 根本不关心您选择的设置,它将始终使用虚拟 RID。 (在我的情况下,即使我在 Windows 10 上,我总是以 win7-x64
文件夹结束......)
2)你project.json
中的platform setting也完全没用
"buildOptions":
"platform": "x64"
3)如果您仅设置win
和/或win-x64
,则在runtimes settings中
"runtimes":
"win": ,
"win-x64":
Visual Studio 将改为使用 win7-x64
。但是,如果您在 Windows 10 计算机上添加 win10-x64
,则将使用它
4) 如果您使用这样的通用 RID 编译应用程序
dotnet build -c debug -r win
然后您的targets
文件将收到您机器的架构(在我的情况下为 x64),而不是我所期望的 AnyCPU
5) 如果您遵循架构runtimes/RID/native
6) 在我的包中只有本机库的情况下,选择的 RID 将始终为 win-x64
使用 Visual Studio 构建,正如我告诉你的,运行时文件夹始终是 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
输出中打印出来
【讨论】:
如果我可以 +10,我会的,很好的答案。 不确定,但我认为路径必须是 Include="$(MSBuildThisFileDirectory)..\runtimes\ (只有一个 ..\) 因为目前在您的帖子中,它试图在特定包的所有 nuget 版本文件夹所在的目录。 @Dimmerworld 考虑到我在这项任务上花费的大量时间,我觉得很奇怪我犯了这样的错误。不确定这是否是我,或者自 2016 年以来是否发生了一些变化,我更正了我的答案。谢谢【参考方案2】:现在,当您使用dotnet restore
而不是nuget.exe restore
时,本机 DLL 没有被复制到输出目录的问题已经消失。
在我使用 dotnet restore --runtime win-x64 <path_to_the_solution.sln>
时,问题已解决。
【讨论】:
以上是关于从 Nuget 包中自动提取本机和托管 DLL的主要内容,如果未能解决你的问题,请参考以下文章
如何在程序启动之前添加然后从 C# 中提取 Dll? [复制]