visual studio net microsoft descargar nuget nuget-package nuget-spec

studio - Agregue archivos nativos del paquete NuGet al directorio de salida del proyecto



nuget microsoft (9)

Intento crear el paquete NuGet para un ensamblado .Net que pinvoke a un dll Win32 nativo. Necesito empacar tanto el ensamblado como el dll nativo con el ensamblaje agregado a las referencias del proyecto (no hay problema en esta parte) y el dll nativo debe copiarse en el directorio de salida del proyecto o en algún otro directorio relativo.

Mis preguntas son:

  1. ¿Cómo empaco el dll nativo sin que el estudio visual intente agregarlo a la lista de referencias?
  2. ¿Debo escribir un install.ps1 para copiar el dll nativo? Si es así, ¿cómo puedo acceder al contenido del paquete para copiarlo?

Aquí hay una alternativa que usa los .targets para inyectar el archivo DLL nativo en el proyecto con las siguientes propiedades.

  • Build action = None
  • Copy to Output Directory = Copy if newer

El principal beneficio de esta técnica es que la DLL nativa se copia en el bin/ carpeta de proyectos dependientes de forma transitiva.

Vea el diseño del archivo .nuspec :

Aquí está el archivo .targets :

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <None Include="$(MSBuildThisFileDirectory)/../MyNativeLib.dll"> <Link>MyNativeLib.dll</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> </Project>

Esto inserta MyNativeLib.dll como si fuera parte del proyecto original (pero curiosamente el archivo no está visible en Visual Studio).

Observe el elemento <Link> que establece el nombre del archivo de destino en el bin/ carpeta.


Es un poco tarde, pero he creado un paquete nuget exacto para eso.

La idea es tener una carpeta especial adicional en tu paquete nuget. Estoy seguro de que ya sabes Lib y contenido. El paquete nuget que he creado busca una Carpeta llamada Salida y copiará todo lo que está ahí dentro de la carpeta de salida de los proyectos.

Lo único que tienes que hacer es agregar una dependencia nuget al paquete http://www.nuget.org/packages/Baseclass.Contrib.Nuget.Output/

Escribí una publicación en el blog sobre esto: http://www.baseclass.ch/blog/Lists/Beitraege/Post.aspx?ID=6&mobile=0


Esta es una vieja pregunta, pero tengo el mismo problema ahora, y encontré un cambio que es un poco complicado pero muy simple y efectivo: crear en la carpeta de contenido estándar de Nuget la siguiente estructura con una subcarpeta para cada configuración:

/Content /bin /Debug native libraries /Release native libraries

Cuando empaquete el archivo nuspec, recibirá el siguiente mensaje para cada biblioteca nativa en las carpetas Depurar y Liberar:

Problema: Asamblea fuera de la carpeta lib. Descripción: El conjunto ''Contenido / Contenedor / Depurar / ??????. Dll'' no está dentro de la carpeta ''lib'' y, por lo tanto, no se agregará como referencia cuando el paquete se haya instalado en un proyecto. Solución: muévala a la carpeta ''lib'' si se debe hacer referencia a ella.

No necesitamos esa "solución" porque este es solo nuestro objetivo: que las bibliotecas nativas no se agreguen como referencias de NET Assemblies.

Las ventajas son:

  1. Solución simple sin secuencias de comandos engorrosas con efectos extraños que son difíciles de restablecer en la desinstalación del paquete.
  2. Nuget administra las bibliotecas nativas como cualquier otro contenido cuando instala y desinstala.

Las desventajas son:

  1. Necesita una carpeta para cada configuración (pero generalmente solo hay dos: Depurar y Liberar, y si tiene otro contenido que debe instalarse en cada carpeta de configuración, este es el camino a seguir)
  2. Las bibliotecas nativas deben estar duplicadas en cada carpeta de configuración (pero si tiene diferentes versiones de las bibliotecas nativas para cada configuración, este es el camino a seguir)
  3. Las advertencias para cada dll nativo en cada carpeta (pero, como dije, las advertencias se envían al creador del paquete en el momento del paquete, no al usuario del paquete en el momento de la instalación de VS)

Hay una solución pura de C # que me resulta bastante fácil de usar y no tengo que preocuparme por las limitaciones de NuGet. Sigue estos pasos:

Incluya la biblioteca nativa en su proyecto y establezca su propiedad Acción de compilación en el Embedded Resource .

Pegue el siguiente código en la clase donde puede invocar esta biblioteca.

private static void UnpackNativeLibrary(string libraryName) { var assembly = Assembly.GetExecutingAssembly(); string resourceName = $"{assembly.GetName().Name}.{libraryName}.dll"; using (var stream = assembly.GetManifestResourceStream(resourceName)) using (var memoryStream = new MemoryStream(stream.CanSeek ? (int)stream.Length : 0)) { stream.CopyTo(memoryStream); File.WriteAllBytes($"{libraryName}.dll", memoryStream.ToArray()); } }

Llame a este método desde el constructor estático como sigue UnpackNativeLibrary("win32"); y desempaquetará la biblioteca en el disco justo antes de que lo necesite. Por supuesto, debe asegurarse de tener permisos de escritura para esa parte del disco.


No puedo resolver su problema exacto, pero puedo darle una sugerencia.

Su requisito clave es: "Y no registrar automáticamente la referencia" .....

Por lo tanto, deberá familiarizarse con los "elementos de solución"

Ver referencia aquí:

Agregar elementos de nivel de solución en un paquete NuGet

Tendrás que escribir algo de vudú de PowerShell para obtener la copia de tu dll nativo en su casa (de nuevo, porque NO quieres que se encienda el vudú de auto-agregar-referencia)

Aquí hay un archivo ps1 que escribí ... para poner archivos en una carpeta de referencias de terceros.

Hay suficiente para que descubras cómo copiar tu dll nativo en algún "hogar" ... sin tener que empezar desde cero.

Nuevamente, no es un golpe directo, pero es mejor que nada.

param($installPath, $toolsPath, $package, $project) if ($project -eq $null) { $project = Get-Project } Write-Host "Start Init.ps1" <# The unique identifier for the package. This is the package name that is shown when packages are listed using the Package Manager Console. These are also used when installing a package using the Install-Package command within the Package Manager Console. Package IDs may not contain any spaces or characters that are invalid in an URL. #> $separator = " " $packageNameNoVersion = $package -split $separator | select -First 1 Write-Host "installPath:" "${installPath}" Write-Host "toolsPath:" "${toolsPath}" Write-Host "package:" "${package}" <# Write-Host "project:" "${project}" #> Write-Host "packageNameNoVersion:" "${packageNameNoVersion}" Write-Host " " <# Recursively look for a .sln file starting with the installPath #> $parentFolder = (get-item $installPath) do { $parentFolderFullName = $parentFolder.FullName $latest = Get-ChildItem -Path $parentFolderFullName -File -Filter *.sln | Select-Object -First 1 if ($latest -ne $null) { $latestName = $latest.name Write-Host "${latestName}" } if ($latest -eq $null) { $parentFolder = $parentFolder.parent } } while ($parentFolder -ne $null -and $latest -eq $null) <# End recursive search for .sln file #> if ( $parentFolder -ne $null -and $latest -ne $null ) { <# Create a base directory to store Solution-Level items #> $thirdPartyReferencesDirectory = $parentFolder.FullName + "/ThirdPartyReferences" if ((Test-Path -path $thirdPartyReferencesDirectory)) { Write-Host "--This path already exists: $thirdPartyReferencesDirectory-------------------" } else { Write-Host "--Creating: $thirdPartyReferencesDirectory-------------------" New-Item -ItemType directory -Path $thirdPartyReferencesDirectory } <# Create a sub directory for only this package. This allows a clean remove and recopy. #> $thirdPartyReferencesPackageDirectory = $thirdPartyReferencesDirectory + "/${packageNameNoVersion}" if ((Test-Path -path $thirdPartyReferencesPackageDirectory)) { Write-Host "--Removing: $thirdPartyReferencesPackageDirectory-------------------" Remove-Item $thirdPartyReferencesPackageDirectory -Force -Recurse } if ((Test-Path -path $thirdPartyReferencesPackageDirectory)) { } else { Write-Host "--Creating: $thirdPartyReferencesPackageDirectory-------------------" New-Item -ItemType directory -Path $thirdPartyReferencesPackageDirectory } Write-Host "--Copying all files for package : $packageNameNoVersion-------------------" Copy-Item $installPath/*.* $thirdPartyReferencesPackageDirectory -recurse } else { Write-Host "A current or parent folder with a .sln file could not be located." } Write-Host "End Init.ps1"


Ponlo es la carpeta de contenido

el comando nuget pack [projfile].csproj lo hará automáticamente si marca los archivos como contenido.

a continuación, edite el archivo de proyecto como se menciona aquí, agregando ItemGroup & NativeLibs & None element

<ItemGroup> <NativeLibs Include="$(MSBuildThisFileDirectory)**/*.dll" /> <None Include="@(NativeLibs)"> <Link>%(RecursiveDir)%(FileName)%(Extension)</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>

trabajó para mi


Recientemente tuve el mismo problema cuando traté de crear un paquete EmguCV NuGet que incluye tanto ensamblados administrados como bibliotecas compartidas no administradas (que también tenían que ubicarse en un subdirectorio x86 ) que tuvieron que copiarse automáticamente en el directorio de salida de compilación después de cada construir.

Aquí hay una solución que se me ocurrió, que solo se basa en NuGet y MSBuild:

  1. Coloque los ensamblados administrados en el directorio /lib del paquete (parte obvia) y las bibliotecas compartidas no administradas y los archivos relacionados (por ejemplo, paquetes .pdb) en el subdirectorio /build (como se describe en los documentos NuGet ).

  2. Cambie el nombre de todas las terminaciones de archivo *.dll no administradas a algo diferente, por ejemplo *.dl_ para evitar que NuGet gime de supuestos ensamblajes colocados en un lugar incorrecto ( "Problema: ensamblaje fuera de la carpeta lib" ).

  3. Agregue un archivo personalizado <PackageName>.targets en el subdirectorio /build con algo así como los siguientes contenidos (vea a continuación una descripción):

    <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <AvailableItemName Include="NativeBinary" /> </ItemGroup> <ItemGroup> <NativeBinary Include="$(MSBuildThisFileDirectory)x86/*"> <TargetPath>x86</TargetPath> </NativeBinary> </ItemGroup> <PropertyGroup> <PrepareForRunDependsOn> $(PrepareForRunDependsOn); CopyNativeBinaries </PrepareForRunDependsOn> </PropertyGroup> <Target Name="CopyNativeBinaries" DependsOnTargets="CopyFilesToOutputDirectory"> <Copy SourceFiles="@(NativeBinary)" DestinationFiles="@(NativeBinary->''$(OutDir)/%(TargetPath)/%(Filename).dll'')" Condition="''%(Extension)''==''.dl_''"> <Output TaskParameter="DestinationFiles" ItemName="FileWrites" /> </Copy> <Copy SourceFiles="@(NativeBinary)" DestinationFiles="@(NativeBinary->''$(OutDir)/%(TargetPath)/%(Filename).%(Extension)'')" Condition="''%(Extension)''!=''.dl_''"> <Output TaskParameter="DestinationFiles" ItemName="FileWrites" /> </Copy> </Target> </Project>

El archivo .targets anterior se inyectará en una instalación del paquete NuGet en el archivo de proyecto de destino y es responsable de copiar las bibliotecas nativas en el directorio de salida.

  • <AvailableItemName Include="NativeBinary" /> agrega un nuevo elemento "Build Action" para el proyecto (que también está disponible en el menú desplegable "Build Action" dentro de Visual Studio).

  • <NativeBinary Include="... agrega las bibliotecas nativas ubicadas en /build/x86 al proyecto actual y las hace accesibles al destino personalizado que copia esos archivos en el directorio de salida.

  • <TargetPath>x86</TargetPath> agrega metadatos personalizados a los archivos y le dice al destino personalizado que copie los archivos nativos en el subdirectorio x86 directorio de salida real.

  • El bloque <PrepareForRunDependsOn ... agrega el destino personalizado a la lista de destinos de los que depende la compilación, consulte el archivo Microsoft.Common.targets para obtener más información.

  • El objetivo personalizado, CopyNativeBinaries , contiene dos tareas de copia. El primero es responsable de copiar cualquier *.dl_ al directorio de salida mientras cambia su extensión al *.dll original. El segundo simplemente copia el resto (por ejemplo, cualquier *.pdb ) en la misma ubicación. Esto podría ser reemplazado por una única tarea de copia y una secuencia de comandos install.ps1 que tuvo que cambiar el nombre de todos los archivos *.dl_ a *.dll durante la instalación del paquete.

Sin embargo, esta solución aún no copiará los binarios nativos en el directorio de salida de otro proyecto que hace referencia al que inicialmente incluye el paquete NuGet. Aún debe hacer referencia al paquete NuGet en su proyecto "final" también.


Si alguien más tropieza con esto.

El .targets archivo .targets DEBE ser igual al Id del paquete NuGet

Cualquier otra cosa no funcionará.

Los créditos van a: https://sushihangover.github.io/nuget-and-msbuild-targets/

Debería haber leído más a fondo ya que de hecho se nota aquí. Me llevó siglos ...

Agregue un <PackageName>.targets


El uso del destino Copy en el archivo de destinos para copiar las bibliotecas necesarias no copiará esos archivos a otros proyectos que DllNotFoundException referencia al proyecto, lo que DllNotFoundException resultado una DllNotFoundException . Sin embargo, esto se puede hacer con un archivo de objetivos mucho más simple, utilizando un elemento None , ya que MSBuild copiará todos los archivos None para hacer referencia a los proyectos.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <NativeLibs Include="$(MSBuildThisFileDirectory)**/*.dll" /> <None Include="@(NativeLibs)"> <Link>%(RecursiveDir)%(FileName)%(Extension)</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> </Project>

Agregue el archivo de objetivos al directorio de compilación del paquete nuget junto con las bibliotecas nativas requeridas. El archivo de objetivos incluirá todos los archivos dll en todos los directorios secundarios del directorio de build . Por lo tanto, para agregar una versión x86 y x64 de una biblioteca nativa utilizada por un ensamblado administrado Any CPU terminaría con una estructura de directorios similar a la siguiente:

  • construir
    • x86
      • NativeLib.dll
      • NativeLibDependency.dll
    • x64
      • NativeLib.dll
      • NativeLibDependency.dll
    • MyNugetPackageID.targets
  • lib
    • net40
      • ManagedAssembly.dll

Los mismos directorios x86 y x64 se crearán en el directorio de salida del proyecto cuando se construyan. Si no necesita subdirectorios, se pueden eliminar ** y %(RecursiveDir) y, en su lugar, incluir los archivos necesarios en el directorio de build directamente. Otros archivos de contenido requeridos también se pueden agregar de la misma manera.

Los archivos agregados como None en el archivo de objetivos no se mostrarán en el proyecto cuando se abra en Visual Studio. Si se pregunta por qué no uso la carpeta de Content en nupkg es porque no hay manera de establecer el elemento CopyToOutputDirectory sin utilizar un script de powershell (que solo se ejecutará dentro de Visual Studio, no desde el símbolo del sistema, en servidores de compilación) o en otros IDEs, y no es compatible con proyectos project.json / xproj DNX ) y prefiero usar un Link a los archivos en lugar de tener una copia adicional de los archivos dentro del proyecto.

Actualización: aunque esto también debería funcionar con Content lugar de None , parece que hay un error en msbuild, por lo que los archivos no se copiarán para hacer referencia a proyectos a más de un paso (por ejemplo, proj1 -> proj2 -> proj3, proj3 no los archivos del paquete NuGet de proj1 pero proj2 lo harán).