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

Posted

技术标签:

【中文标题】如何使我的托管 NuGet 包支持 C++/CLI 项目?【英文标题】:How can I make my managed NuGet package support C++/CLI projects? 【发布时间】:2013-09-12 17:57:18 【问题描述】:

我制作了一个 NuGet 包,当我在 C# 项目中使用它时效果很好。它在lib/net40 目录中包含一个 DLL,该 DLL 被添加为引用。

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

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

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

显然,我应该使用 build/native 下的 .props.targets 文件来执行此操作,但我需要在这些文件中添加什么才能使其工作?

【问题讨论】:

我认为这更像是 NuGet 中的一个错误,而不是您需要在包中调整的内容。看到这个:nuget.codeplex.com/workitem/3665另外,这个问题是相关的:***.com/q/18786338/289770 谢谢!有趣的是,在我提出问题的第二天,错误报告是如何发布的。我现在已经投票了。尽管如此,无论有没有错误,我相信使用 NuGet 脚本可以解决这个问题,我正在寻找一个可以做到这一点的指南。 10 个月后,但也许这个 ([***.com/questions/23571241/…) 可能会对您有所帮助。 [1]:***.com/questions/23571241/… 【参考方案1】:

作为Patrick O'Hara wrote,NuGet 不会为您更改 C++/CLI 项目。见GitHub Issue NuGet/Home#1121 - Cannot install managed packages into a CLI project。但是,使用 NuGet 命令行实用程序 NuGet.exe,您可以让 NuGet 下载并解压缩所需的包。

对于一个完整的示例,以下是我在 Visual Studio 2013 C++/CLI 项目中添加对 OptimizedPriorityQueue 1.0.0 的引用的步骤:

    如果尚未打开包管理器控制台,请打开(工具 > NuGet 包管理器 > 包管理器控制台)。

    在包管理器控制台中,安装 NuGet.CommandLine 包:

    安装包 NuGet.CommandLine

    (注意:在撰写本文时,NuGet.CommandLine 的最新版本是 2.8.6。您可能会有所不同。)

    在您的项目文件夹中,现在应该有一个 .nuget\packages.config XML 文件,其内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="NuGet.CommandLine" version="2.8.6" />
    </packages>
    

    在 Notepad++ 等文本编辑器中,为所需的包添加 &lt;package&gt; 元素。在这种情况下,我补充说:

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

    .. 在&lt;packages&gt; 元素内。

    打开命令提示符(我打开了一个 VS2013 开发者命令提示符,但应该可以使用常规命令提示符。)

    cd 进入项目文件夹。

    运行以下命令,如果不同,则更改 NuGet.CommandLine 的版本号:

    .\packages\NuGet.CommandLine.2.8.6\tools\NuGet.exe 安装 -NonInteractive -OutputDirectory 包 .nuget\packages.config

    对我来说,输出是:

    安装“OptimizedPriorityQueue 1.0.0.0”。 成功安装“OptimizedPriorityQueue 1.0.0.0”。 packages.config 中列出的所有软件包都已安装。 右键单击 Visual Studio 中的项目并选择 属性。在 Common Properties > References 下,点击 Add New Reference... 按钮。 选择左侧的浏览。在 Add Reference 对话框的 OK 和 Cancel 按钮旁边,有一个 Browse... 按钮。点击打开文件选择对话框。 导航到 NuGet 解压到项目文件夹的 packages 子目录的 DLL,然后单击 添加 按钮。单击确定关闭“添加引用”对话框。

    您现在应该可以在您的 C++/CLI 项目中使用该程序集了:

    using namespace Priority_Queue;
    
    //...
    

【讨论】:

@daniel-trebbien,你是如何处理包更新的? @daniel-trebbien,任何线索如何在 Visual Studio 2017 中执行步骤 8 到 10? 这在 Visual Studio 2013 中非常适合我。我更喜欢这个解决方案,而不是创建一个 C# 虚拟项目并将 dll 从一个项目复制到另一个项目。我能够添加 NLog 4.0.0。 非常有帮助,谢谢!仅供参考,当我在 VS 2019 中执行此操作时,我不得不稍微更改第 7 步命令,packages.config 是在项目的基本目录中创建的,而不是 .nuget 文件夹。此外,要在步骤 8 中将“浏览”作为选项查看,我必须确保我的项目已配置为“公共语言运行时支持 (/clr)”hth, X 第 2 步给了我与问题相同的错误。【参考方案2】:

实际上似乎有可能使用以下步骤(至少使用NuGet &gt;= 2.5)从 C++/CLI 项目中安装和自动引用“常规”NuGet 包:

    在要打包的项目中添加(或修改)build\&lt;ProjectName&gt;.targets 文件并将以下内容放入其中(确保将&lt;AssemblyName&gt; 替换为实际值):

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

    在打包项目的.nuspec 中添加一个或多个file 条目以将程序集也放置在目标机器的lib\native\ 目录中:

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

即使 NuGet 不向 C++/CLI 项目添加程序集引用,它仍会插入包提供的任何 .props.targets 文件。第 1 步中的自定义目标将添加对我们打包程序集的引用。

据我所知,此解决方案的一个缺点是以这种方式添加的引用未显示在 C++/CLI 项目的Commpon Properties/Framework and References 部分中。可能还有其他人,所以使用它需要您自担风险...

【讨论】:

【参考方案3】:

正如对此端口 (Nuget won't install Entity Framework into C++/CLI project) 的答复中所述,NuGet 不会为您更改 C++/CLI 项目。但是,它将为您下载并解包依赖项。我们从命令行使用它作为我们的 make 依赖项的一部分。命令行将如下所示:

/.NuGet/NuGet.exe
      Install 
      -NonInteractive 
      -ConfigFile $ENVSRC_ROOT/.nuget/NuGet.config 
      -OutputDirectory $ENVSRC_ROOT/packages 
      $ENVSRC_ROOT/packages.config

请注意,命令行参数是逐行分隔的,以便于阅读。我们还决定在 .NuGet 文件夹中将 NuGet 检入我们的源代码控制中。目标是更容易为我们的各种环境(并非所有环境都使用 Visual Studio)设置构建机器。首次运行此命令后,您必须手动将依赖项添加到 C++/CLI 项目中。 希望对您有所帮助。

【讨论】:

看起来如果我的 C++/CLI 项目是类库(程序集)而不是 exe,nuget 仍然失败。 @WeipengL 这是一个很老的问题。您的回复缺乏细节。也许您应该创建一个新问题来描述您的问题并参考这个问题?【参考方案4】:

简单的解决方法是将此类 NuGet 包装在常规 .NET 项目 (C#) 中,并在 C++/CLI 项目中引用相同的内容。

【讨论】:

【参考方案5】:

安装程序尝试在 C# 启动项目中添加对自身的引用。在安装之前将 C# 项目作为解决方案中的启动项目。如果没有,请创建一个虚拟 C# 项目

【讨论】:

C# 与 C++/CLI 不同【参考方案6】:

我在这里的解决方案不会更容易为 Cli 项目添加 Nuget 包支持,但它允许我将 nuget 包添加到我的 Cli 包中。

我收到错误消息:“您正在尝试将此包安装到以 'native,Version=v0.0' 为目标的项目中,但该包不包含 任何与该框架兼容的程序集引用或内容文件。”当我尝试安装 OptiPlot.WPF 或 NuGet.Commandline 时。

这可能不是满足每个人需求的完整解决方案,但它对我有用。我发现了一种将 OptiPlot.WPF 放入示例 C++.Net 项目的“作弊”方式。我的解决方案中有一个 C# 主程序项目 - 我在那里安装了包: 安装包 OxyPlot.Wpf -Version 2.0.0

然后我将 packages.config 文件和 packages 文件夹从那里复制到我的 C++ .Net 类库项目中。然后我编辑了packages.config文件,取出了一些不适用的东西,我可能在不需要的包中取出了一个包。然后我将 C++ .Net 类库项目中的引用添加到 packages/OxyPlot.Wpf.2.0.0 文件夹。

现在我可以在 C++ 中使用 OxyPlot!酷!

【讨论】:

【参考方案7】:

上面的答案存在二级依赖问题(至少在我的 Visual Studio 2019 上)。

为了解决这个问题,我通常会创建一个空的 c# 控制台应用程序并引用那里的所有包。

然后我使用这篇文章 build sn-p 将除项目主要工件之外的所有内容复制到解决方案文件夹中名为 packages 的公共存储中。

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

整个packages 文件夹随后在 c++/cli 项目中使用自定义任务进行部署。

如果引用的包以 AnyCPU 为目标,则此解决方案适用,否则需要对每个处理器架构使用不同的文件夹进行一些修改,并且可能无法正常工作。

这个解决方案并不优雅,但解决了从 c++/cli 项目可靠地使用 nuget 包(间接)的目的。

与此处发布的其他解决方案相比,此解决方案的优点是路径没有版本控制,因此在正常包升级期间不会更改 c++/cli 项目。

【讨论】:

【参考方案8】:

凭据实际上是使用添加包源的机器密钥加密的。除非使用明文变体,否则 setApiKey 命令可能应该作为构建的一部分运行。

【讨论】:

以上是关于如何使我的托管 NuGet 包支持 C++/CLI 项目?的主要内容,如果未能解决你的问题,请参考以下文章

具有托管依赖项的C ++ / XAML UWP NuGet包

如何创建托管在 Gitlab 上的 npm CLI 包

如何从我的 C++/CLI 代码进入非托管 C++ 库

C++/cli 错误 C4368:无法将“列表”定义为托管“队列”的成员:不支持混合类型

如何托管我自己的nuget v3 Feed?

如何在单个nuget cli命令中将多个包更新到所需的版本?