Where to place dlls for unmanaged libraries?

2020-02-26 03:59发布

问题:

I am trying to create a Nuget package for a library that depends on ghostscript and therefore references gsdll32.dll - an unmanaged library. I can't just included that a standard dll reference. Where do I put this in the nuget directory structure?

回答1:

Add a build folder to the package and, if the package for example has the id MyPackage, add a MSBuild target file called MyPackage.targets to this folder. It is important that the .targets file has the same name as the .nuspec file. In the .nuspec file you must have a section like this:

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

This will add an MSBuild element in the project file pointing to the .targets file.

Furthermore, to only register the managed dlls, add a section like this:

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

The .targets file should look something like this:

<?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>

Now, all files - including unmanaged files - will be copied to the project output folder (e.g. \bin\debug) after the build.



回答2:

The above reference can work, but it actually modifies your post build event to push files over, which may not actually fix your issue if you have the situation we did.

The issue we were having was a dependent DLL could not be registered, but had to exist side by side with another DLL which needed to be registered by nuget so it needed to exist in the lib directory but not be registered.

The nuspec reference now allows you to specify which DLLs in the lib directory get explicitly registered in the visual studio project now, you simply need to add into your nuspec file in the metadata area an explicit references list (if this does not exist the default behavior of nuget is to attempt to register everything under lib).

Here is an example nuspec file of what I mean:

<?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>

As you can see, ceTe.DynamicPDF.Rasterizer.20.x86.dll needs to be registered, but DPDFRast.x86.dll simply needs to exist in that directory to support the other DLL and won't be registered but through some dynamic referencing magic will ultimately be copied over into the destination bin directory anyway because visual studio sees that the first DLL is dependent upon the second.

Here is the original nuspec reference.



回答3:

Response on the Nuget forum: http://nuget.codeplex.com/discussions/352689

pranavkm: The SQLCE package has a similar issue that we handle via PS scripts. Checkout out the scripts at https://bitbucket.org/davidebbo/nugetpackages/src/1cba18b864f7/SqlServerCompact/Tools.



回答4:

I largely got this to work using Lars Michael's method, but one thing I needed to add comes from James Eby's answer. Visual Studio was trying to register all the dll's in my lib directory, so I added a references element to the metadata in the nuspec file to tell it to only register the managed dll:

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

Also in

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

I first tried the id of my package FANNCSharp-x64, but it needed the full package name: FANNCSharp-x64.0.1.4.



回答5:

One problem I had was that the packages path wasn't always in the same place relative to the project file. The following worked for me:

  1. Within the NuGet package, place your unmanaged DLLs in the lib\native folder.

  2. Add the following script to the tools folder:

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)
  1. Add a targets file to the build folder. (Change "MyPackage" to the name of your package). Using a unique name for the target, like "CopyMyPackage", avoids conflicts with other packages trying to define the "AfterBuild" target. This targets file makes use of the $(PackagesPath) property defined by the above script.

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. Finally, add a "MyPackageReadMe.txt" to the Content folder. This will enable the package to install.

See also: http://alski.net/post/2013/05/23/Using-NuGet-25-to-deliver-unmanaged-dlls.aspx