非托管库的dll应该放在哪里?

27

我正在尝试为一个依赖于Ghostscript的库创建Nuget包,因此需要引用gsdll32.dll - 一种非托管库。我不能只将其作为标准dll引用程序包含在内。我应该将它放在Nuget目录结构的哪个位置?

6个回答

19

在软件包中添加一个名为build的文件夹,如果软件包的ID为MyPackage,请将一个名为MyPackage.targets的MSBuild目标文件添加到此文件夹中。重要的是,.targets 文件与 .nuspec 文件具有相同的名称。在 .nuspec 文件中,你必须有以下类似的部分:

<files>
    <file src="lib\*.*" target="lib" />
    <file src="build\MyPackage.targets" target="build" />
</files>

这将在项目文件中添加一个 MSBuild 元素,指向 .targets 文件。

此外,要仅注册托管的 dll,可以添加如下部分:

<references>
    <reference file="MyManaged.dll" />
</references>

.targets 文件应该长成这个样子:

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
  <Target Name="CopyMyPackageFiles" AfterTargets="AfterBuild"> 
    <ItemGroup> 
      <MyPackageFiles Include="$(MSBuildThisFileDirectory)..\lib\*.*"/> 
    </ItemGroup> 
    <Copy SourceFiles="@(MyPackageFiles)" DestinationFolder="$(OutputPath)" > 
    </Copy> 
  </Target> 
</Project>

现在,包括未受管理的文件在内的所有文件将在构建后复制到项目输出文件夹中(例如\bin\debug)。


我很感激这个Lars - 你能把这个编辑到你的答案里吗?博客链接往往会失效。 - George Mauer
1
@Jérôme,虽然有点晚了,但我认为你的问题仍然值得回答。关于.target文件,有一个微小(绝对不明显)的陷阱:名称必须与包名称相同。当这样时,在安装包时,目标会自动添加到.csproj文件中。您可以检查您的<Import>引用您的目标的软件包,通常在末尾附近。只有这样才能正常工作,没有其他魔法涉及。 - Andriy K
2
您可以按照以下方式避免硬编码精确路径: <MyPackageFiles Include="$(MSBuildThisFileDirectory)..\myfile.dll - Matthew Sharpe
@Matt 感谢您的建议,解决了Homr提到的处理软件包版本号的问题。我已相应地修改了答案。 - Lars Michael
2
@ElliotWoods 在 .nuspec 文件中(https://learn.microsoft.com/en-us/nuget/reference/nuspec),.targets 文件需根据您放置本地文件的位置进行自定义。例如,它们放置在名为 lib 的文件夹中。 - Lars Michael
显示剩余7条评论

9
上述参考可以起作用,但实际上它修改了您的发布后事件以推送文件,如果您遇到我们遇到的情况,则实际上可能无法解决您的问题。我们遇到的问题是一个依赖的DLL无法注册,但必须与另一个需要通过nuget注册的DLL并存,因此它需要存在于lib目录中但不需要注册。
现在,nuspec引用允许您指定在Visual Studio项目中显式注册哪些在lib目录中的DLL。您只需在nuspec文件的metadata区域中添加一个明确的references列表即可(如果不存在,则nuget的默认行为是尝试注册lib下的所有内容)。
这是一个示例nuspec文件:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>SomePackageID</id>
        <version>1.0.1</version>
        <title>Some Package Title</title>
        <authors>Some Authors</authors>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Blah blah blah.</description>
        <references>
            <reference file="ceTe.DynamicPDF.Rasterizer.20.x86.dll" />          
        </references>
    </metadata>
    <files>
        <file src="\\SomeNetworkLocation\ceTe.DynamicPDF.Rasterizer.20.x86.dll" target="lib\ceTe.DynamicPDF.Rasterizer.20.x86.dll" />
        <file src="\\SomeNetworkLocation\DPDFRast.x86.dll" target="lib\DPDFRast.x86.dll" />
    </files>
</package>

正如您所见,需要注册ceTe.DynamicPDF.Rasterizer.20.x86.dll,而DPDFRast.x86.dll只需存在于该目录中以支持其他DLL,不需要注册,但通过一些动态引用魔法最终将被复制到目标bin目录中,因为Visual Studio看到第一个DLL依赖于第二个DLL。这是原始的nuspec参考

1
“……通过一些动态引用的魔法,最终将被复制到目标 bin 目录中……”这似乎对于未托管的 DLL 没有起作用,这也是问题所在。 - Lars Michael
3
在我的情况下并没有起作用,但是https://dev59.com/3Wkw5IYBdhLWcg3wBV3j#33041626有效。 - Lars Michael

3

我主要是使用Lars Michael的方法来实现这个,但是我需要添加一个功能,这个功能来源于James Eby的回答。Visual Studio试图注册我lib目录中的所有dll,所以我在nuspec文件的元数据中添加了一个references元素,告诉它只注册托管的dll:

<references>
    <reference file="FANNCSharp.dll" />          
</references>

同时,在

<MyPackageFiles Include="$(MSBuildProjectDirectory)\..\Packages\MyPackage\lib\*.*"/>

我最初尝试使用我的包的ID FANNCSharp-x64,但它需要完整的包名称:FANNCSharp-x64.0.1.4


1
我修改了原来的答案,以解决你提到的问题。谢谢。 - Lars Michael

3

1

我曾经遇到的一个问题是,包路径相对于项目文件的位置并不总是一样。以下方法适用于我:

  1. 在NuGet包中,将非托管的DLL文件放在lib\native文件夹中。

  2. 将以下脚本添加到tools文件夹中:

install.ps1

#This script creates or updates a PackagesPath property in the project file
param($installPath, $toolsPath, $package, $project)

$project.Save()

#Load the csproj file into an xml object
[xml] $xml = Get-Content -path $project.FullName

#grab the namespace from the project element 
$nsmgr = New-Object System.Xml.XmlNamespaceManager -ArgumentList $xml.NameTable
$nsmgr.AddNamespace('a',$xml.Project.GetAttribute("xmlns"))

#find or create the property
$property = $xml.Project.SelectSingleNode("//a:PropertyGroup//a:PackagesPath", $nsmgr)
if (!$property)
{
    $property = $xml.CreateElement("PackagesPath", $xml.Project.GetAttribute("xmlns"))
    $propertyGroup = $xml.CreateElement("PropertyGroup", $xml.Project.GetAttribute("xmlns"))
    $propertyGroup.AppendChild($property)
    $xml.Project.InsertBefore($propertyGroup, $xml.Project.ItemGroup[0])
}

#find the relative path to the packages folder
$absolutePackagesPath = (get-item $installPath).parent.FullName
push-location (split-path $project.FullName)
$relativePackagesPath = Resolve-Path -Relative $absolutePackagesPath
pop-location

#set the property value
$property.InnerText = $relativePackagesPath

#save the changes.
$xml.Save($project.FullName)

在构建文件夹中添加一个目标文件。(将"MyPackage"更改为您的包名称)。使用唯一的名称作为目标,比如"CopyMyPackage",可以避免与其他试图定义"AfterBuild"目标的包发生冲突。这个目标文件利用了上面脚本定义的$(PackagesPath)属性。

MyPackage.targets

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
  <Target Name="CopyMyPackage" AfterTargets="AfterBuild"> 
    <ItemGroup> 
      <MyPackageSourceFiles Include="$(PackagesPath)\MyPackage.*\lib\native\*.*"/> 
    </ItemGroup> 
    <Copy SourceFiles="@(MyPackageSourceFiles)" DestinationFolder="$(OutputPath)" > 
    </Copy> 
  </Target> 
</Project>
  1. 最后,在Content文件夹中添加一个“MyPackageReadMe.txt”文件。这将使包能够安装。

另请参阅: http://alski.net/post/2013/05/23/Using-NuGet-25-to-deliver-unmanaged-dlls.aspx


0
对于 .NET Core,如果您知道本机代码的运行时平台,那么这就非常简单。当您构建时,您可能会注意到在 bin 树下的 .NET Core 构建文件夹中有一个名为“runtimes”的文件夹。它看起来像这样:

runtime platform folders in .NET Core

这些文件夹旨在保存任何特定于平台的内容,包括未管理/本地 DLL。
在您的 NuGet 包中,在“文件”部分下添加以下内容:
<file src="[source path for file in package]" target="runtimes\[platform]\native\[file name]" />

当执行应用程序时,运行时环境将在相应的平台目录中查找未管理的 dll 文件。

如果您想要针对多个平台进行定位,请为每个平台添加另一个文件条目。


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