我如何使我的管理 NuGet 包支持 C + +/CLI 项目?

我已经制作了一个 NuGet 包,当我从一个 C # 项目中使用它时,它工作得很好。它在 lib/net40目录中包含一个 DLL,并添加 DLL 作为引用。

既然 NuGet 支持 C + + ,那么我实际上如何修改我的包,以便 DLL 可以作为 C + +/CLI 项目中的托管引用添加?我找不到任何教程来解释这一点。如果我试图按原样添加包,就会得到以下错误:

您正在尝试将此包安装到以“本机,版本 = v0.0”为目标的项目中,但该包不包含与该框架兼容的任何程序集引用或内容文件。

人们可能认为解决方案是将文件放在 lib/national 下,但是根据 http://docs.nuget.org/docs/reference/support-for-native-projects,这是不受支持的。而且,简单地将 DLL 直接放在 lib 下似乎没有任何作用。

显然,我应该使用构建/本机下的 .props.targets文件执行此操作,但是我需要在这些文件中放入什么才能使其工作呢?

35076 次浏览

Credentials are actually encrypted with the machinekey where the package source was added. Unless using the plaintext variant, the setApiKey command should probably be run as part of the build.

As mentioned in the answer to this port (Nuget won't install Entity Framework into C++/CLI project), NuGet will not make the changes to a C++/CLI project for you. It will however download and unpackage the dependency for you. We use it from the command line as part of our make dependencies. The command line will look something like this:

/.NuGet/NuGet.exe
Install
-NonInteractive
-ConfigFile $ENV{SRC_ROOT}/.nuget/NuGet.config
-OutputDirectory $ENV{SRC_ROOT}/packages
$ENV{SRC_ROOT}/packages.config

Note that the command line areguments are separated one to a line to make reading easier. Also we decided to check NuGet into our source control un the .NuGet folder. The goal was to make it easier to setup a build machine for our various environments (not all of which use Visual Studio). Once you have run this command for the first time, you must manually add the dependencies to your C++/CLI project.
Hope that helps.

As Patrick O'Hara wrote, NuGet will not make changes to a C++/CLI project for you. See GitHub Issue NuGet/Home#1121 - Cannot install managed packages into a CLI project. However, using the NuGet command line utility, NuGet.exe, you can have NuGet download and unpack the desired package(s).

For a complete example, here were steps that I took to add a reference to OptimizedPriorityQueue 1.0.0 in a Visual Studio 2013 C++/CLI project:

  1. Open the Package Manager Console if not already open (TOOLS > NuGet Package Manager > Package Manager Console).
  2. In the Package Manager Console, install the NuGet.CommandLine package:

    Install-Package NuGet.CommandLine
    

    (Note: As of this writing, the latest version of NuGet.CommandLine is 2.8.6. It may be different for you.)

  3. Within your project folder, there should now be a .nuget\packages.config XML file with the following contents:

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
    <package id="NuGet.CommandLine" version="2.8.6" />
    </packages>
    
  4. In a text editor such as Notepad++, add a <package> element for the desired package. In this case, I added:

    <package id="OptimizedPriorityQueue" version="1.0.0" />
    

    .. within the <packages> element.

  5. Open a command prompt (I opened a VS2013 Developer Command Prompt, but a regular command prompt should work.)

  6. cd into the project folder.
  7. Run the following command, changing the version number of NuGet.CommandLine if different:

    .\packages\NuGet.CommandLine.2.8.6\tools\NuGet.exe Install -NonInteractive -OutputDirectory packages .nuget\packages.config
    

    For me, the output was:

    Installing 'OptimizedPriorityQueue 1.0.0.0'.
    Successfully installed 'OptimizedPriorityQueue 1.0.0.0'.
    All packages listed in packages.config are already installed.
    
  8. Right click on the project in Visual Studio and select Properties. Under Common Properties > References, click the Add New Reference… button.
  9. Select Browse on the left hand side. Next to the Add Reference dialog's OK and Cancel buttons, there is a Browse… button. Click that to open a file selection dialog.
  10. Navigate to the DLLs that NuGet unpacked to the packages subdirectory of your project folder and click the Add button. Click OK to close the Add Reference dialog.
  11. You should now be able to use the assembly in your C++/CLI project:

    using namespace Priority_Queue;
    
    
    //...
    

There seem to be actually a possibility to enable "regular" NuGet packages to be installed and automatically referenced from C++/CLI projects using following steps (at least with NuGet >= 2.5):

  1. Add (or modify) a build\<ProjectName>.targets file to your project to be packaged and put following content into it (make sure to replace <AssemblyName> with an actual value):

    <?xml version="1.0" encoding="utf-8" ?>
    <Project ToolsVersion="4.0"
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!-- for C++/CLI projects only -->
    <ItemGroup Condition="'$(Language)' == 'C++'">
    <Reference Include="<AssemblyName>">
    <!--
    this .targets file is installed next to the assembly,
    so we do not have to figure out any versions or paths here ourselves
    -->
    <HintPath>
    $(MSBuildThisFileDirectory)..\lib\native\<AssemblyName>.dll
    </HintPath>
    </Reference>
    </ItemGroup>
    </Project>
    
  2. In the .nuspec of the packaged project add one or more file entries to also place the assembly in lib\native\ directory at the target machine:

    <package>
    <metadata>
    ...
    </metadata>
    <files>
    ...
    <!--
    add a copy of the assembly to lib\native to prevent NuGet
    from complaining about incompatible native projects
    -->
    <file src="bin\$configuration$\$id$.dll" target="lib\native\" />
    <file src="bin\$configuration$\$id$.xml" target="lib\native\" />
    
    
    <!-- don't forget about the .targets file containing the reference -->
    <file src="build\$id$.targets" target="build\" />
    </files>
    ...
    </package>
    

Even if NuGet does not add assembly references to C++/CLI projects, it still inserts any .props and .targets files provided by a package. And the custom target from step 1 will add a reference to our packaged assembly.

One drawback of this solution, as far as I could see it, is that the reference added in such a way is not displayed in the Commpon Properties/Framework and References section of the C++/CLI project. There may also be others, so use it at your own risk...

The installer tries to add a reference to itself in the C# startup project. Make a C# project the startup project in the solution before install. Create a dummy C# project if you do not have one

Easy workaround is to wrap such NuGet in a regular .NET project (C#) and reference the same in your C++/CLI project.

The answers above have issues with second-level dependencies (at least in my case on Visual Studio 2019).

To solve the issue, I usually create an empty c# console application and reference all the packages there.

Then I use this post build snippet to copy everything except the project main artifacts to a common store named packages in the solution folder.

  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<PropertyGroup>
<SharedLibraries>$(SolutionDir)\packages</SharedLibraries>
</PropertyGroup>


<ItemGroup>
<Artifacts Include="$(OutDir)\**" />
</ItemGroup>


<RemoveDir Condition=" Exists('$(SharedLibraries)')" Directories="$(SharedLibraries)" />
<MakeDir Condition=" !Exists('$(SharedLibraries)')" Directories="$(SharedLibraries)" />


<Copy SourceFiles="@(Artifacts)" DestinationFolder="$(SharedLibraries)\%(RecursiveDir)" />


<ItemGroup>
<ExtraFiles Include="$(SharedLibraries)\$(ProjectName).*"></ExtraFiles>
</ItemGroup>


<Delete Files="@(ExtraFiles)" />


</Target>

The whole packages folder is then deployed with a custom task in the c++/cli project.

This solution is suitable if the referenced packages target AnyCPU otherwise some tinkering is necessary to use different folders for each processor architecture, and probably won't work.

This solution is not elegant, however solves the purpose to reliable consume nuget packages (indirectly) from a c++/cli project.

The plus of this solution with respected to other ones posted here, is that paths are not versioned, therefore c++/cli projects won't ever be changed during normal package upgrade.

My solution here doesn't make it easier to add Nuget package support for Cli projects, but it allows me to add in a nuget package into my Cli Package.

I was getting the error: "You are trying to install this package into a project that targets 'native,Version=v0.0', but the package does not contain any assembly references or content files that are compatible with that framework." when I tried to install OptiPlot.WPF or the NuGet.Commandline.

This may not be a complete solution for everyone's needs, but it worked for me. I found a "cheatin" way of getting OptiPlot.WPF into a sample C++.Net project. I had a C# main program project in my solution - I installed the package there: Install-Package OxyPlot.Wpf -Version 2.0.0

Then I copied the packages.config file and the packages folder from there to my C++ .Net Class Library Project. Then I edited the packages.config file and took something out that didn't apply, and I probably took out a package in packages that wasn't needed. Then I added my references in the C++ .Net Class Library Project to packages/OxyPlot.Wpf.2.0.0 folder.

Now I could use the OxyPlot inside of C++! Cool!