如何添加对由 C# 项目调用的非托管 C++ 项目的引用?
Posted
技术标签:
【中文标题】如何添加对由 C# 项目调用的非托管 C++ 项目的引用?【英文标题】:How do I add a reference to an unmanaged C++ project called by a C# project? 【发布时间】:2011-07-03 17:20:47 【问题描述】:一个解决方案(the.sln)
一个 C++ 项目(2010 年为 mycppproject.vcxproj 或 2008 年为 mycppproject.vcproj),它编译导出一些函数的本机 DLL。在调试中构建 c:\output\Debug\mycppproject_d.dll,在发布中构建 c:\output\Release\mycppproject.dll。
一个 C# 控制台应用程序 (mycsharpconsole.csproj),其中包含对 DLL 的 PInvoke 调用。
一切正常。
当我构建时,我希望能够将 csharp 项目的引用添加到 cpp DLL 项目,以便它可以将相应目录中的相应文件复制到 csharp 项目构建的 \bin\Debug 目录中进入。
这应该是可能的,因为 IDE 知道关于 DLL 在哪里构建以及 C# 应用程序在哪里构建的所有信息。
在 Visual Studio 2010 中:
我在 csharp 项目上尝试了“依赖项...”并添加了对 mycppproject 的依赖项,但没有效果。
我已尝试在 csharp 项目上“添加引用...”并添加对 cpp 项目的引用,但我收到一条警告消息“项目“mycppproject”的目标框架版本高于当前项目目标框架版本。是否仍要将此引用添加到您的项目中? (是/否/取消)。
单击“是”会产生错误消息“无法添加对 mycppproject 的引用。”
【问题讨论】:
Add a Reference from a C# App to a DLL compiled without /clr? 的可能重复项 抱歉,这确实似乎与该问题重复。在我的初始搜索中找不到它。 【参考方案1】:您不能添加对非托管 DLL 的引用。
相反,您应该执行构建后任务来复制文件。
或者,您可以将指向非托管 DLL 的链接作为文件添加到 C# 项目中,并将 Build Action
设置为 None
并将 Copy to Output Directory
设置为 Copy If Newer
。
【讨论】:
如果我使用你的第二个建议,如果 C++ dll 被重建,它会选择新文件吗?在您将文件作为链接添加到项目中时,它似乎会复制该文件。 @Matt:从打开对话框的下拉列表中选择Add as Link
。
这对于发布和调试等不同的构建配置没有帮助。
@WaffleSouffle:使用文本编辑器打开您的 C# 项目的 *.csproj 文件并搜索所有出现的“YourNativeCppProject.dll”(如果您将 *.pdb 文件作为链接添加,您会发现更多不止一次),并使用宏更改包含路径,例如: Include="$(SolutionDir)$(ConfigurationName)\YourNativeCppProject.dll" PS:如果您查看属性 (F4),VS 会向您显示 Debug 的路径即使你切换到Release配置,但是如果你编译,你会看到复制到输出的dll是发布版本
我实际上更喜欢将构建操作设置为“内容”。这样,依赖的 DLL 会自动复制到 Setup & Deployment 项目、2nd-tier 应用程序(依赖于包含原始 DLL 依赖项的库)等的输出目录。它只占更多用例。
【参考方案2】:
Visual Studio 不支持从托管 C# 引用非托管 C++ 项目,但 MSBuild 支持从任何其他项目引用任何项目。
您可以通过手动编辑 .csproj 文件来手动添加对项目的引用。在文件中,找到您现有的一组 ProjectReference
元素(如果没有,则添加新的 ItemGroup
)并添加以下参考:
<ProjectReference Include="..\mycpproject.csproj">
<Project>b402782f-de0a-41fa-b364-60612a786fb2</Project>
<Name>mycppproject</Name>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Content</OutputItemType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ProjectReference>
执行构建时,引用将导致 MSBuild 首先构建引用的项目。 ReferenceOutputAssembly
值告诉 MSBuild 不要复制构建的输出程序集(因为 C++ 项目不生成),但 OutputItemType
和 CopyToOutputDirectory
值指示它将输出内容复制到引用项目的输出文件夹.
您将能够在 Visual Studio 中看到该参考,但在那里您无法使用它。
此答案基于 Kirill Osenkov 在其 MSDN 博客上解决的类似问题:https://blogs.msdn.microsoft.com/kirillosenkov/2015/04/04/how-to-have-a-project-reference-without-referencing-the-actual-binary/
【讨论】:
这,IMO,比使用构建后复制步骤或插入链接更惯用。 (1) 与手动副本不同,它正确处理“传递”情况。如果 A 引用 B 引用 C 引用 D 引用 native.dll,为什么我必须告诉 A 将 native.dll 复制到其输出目录中? A 甚至不需要知道 native.dll 的存在。 (2) 自动选择debug/release/etc。根据需要使用本机 dll 的版本。没有简单的方法可以通过链接获得该行为。很遗憾 VS 不允许您在 UI 中以这种方式进行设置。 但不幸的是,这种方法似乎在传递依赖的情况下混淆了 VS(托管 A 依赖于托管 B 依赖于本机 C)——当你这样做时,它会将本机 dll 复制到最终输出目录一个干净的构建,但在后续构建中,它会从那里主动删除它。 :( 这对我不起作用。该引用确实显示在 Visual Studio 中,但没有任何程序集/文件被复制。 更正:内容文件被正确复制,但新编译的工件没有。【参考方案3】:我会关注Slaks' second answer...
[...] 您可以将非托管 DLL 的链接作为文件添加到 C# 项目中,并将 Build Action 设置为 None 并将 Copy to Output Directory 设置为 Copy If Newer。 p>
... 后跟 my comment,以区分 Debug 和 Release 构建(即使有点“hackish”,因为它需要您手动编辑 C# 项目文件)
使用文本编辑器打开 C# 项目的 csproj 文件并搜索所有出现的“YourNativeCppProject.dll”(没有“.dll”子前缀,因此如果您也将 pdb 文件添加为链接,您会发现多次出现),并使用宏更改包含路径,例如:Include="$(SolutionDir)$(ConfigurationName)\YourNativeCppProject.dll
PS:如果你查看属性(F4),即使你切换到Release配置,VS也会显示Debug的路径,但是如果你编译,你会看到复制到输出的dll是发布版本*
【讨论】:
在我看来,编辑 .csproj 文件没什么不好。更多的要求。 @WaffleSouffle “有点不受 Visual Studio 和属性窗口管理”,所以:D 这很好用。唯一的问题是,除非您手动构建 C++ 项目,否则即使在正确设置项目依赖项的情况下,本地 DLL 的代码更改时也不会构建。您对此有解决方案吗? @jnm2 不,很抱歉,但很奇怪:我预计 c# 项目会出现构建问题,但如果您修改项目 A,A 应该无论如何都会构建(但一切皆有可能,因为这是一个黑客)。唯一想到(但您可能已经检查过):本机项目是否在配置管理器上检查了“构建”标志? 我反其道而行之;构建“启动”c#项目确实得到了依赖于本地dll的构建,但它不会复制它,因为c#项目是最新的。十年来,我在托管和本机插件(运行时依赖)dll 上都遇到了这个问题。我确实希望 MS 能够改进对这种开发模型的支持。仍然在 VS2015 中达到它:(【参考方案4】:Add Rederences 仅适用于 .NET 程序集、同一解决方案中的 .net 项目或 COM 组件(无论如何都会为其创建管理器包装器...)。
在您的 C# 代码中您使用 DLLImport 访问 C++ dll 的事实并不意味着 Visual Studio 会知道应该从哪里复制外部 dll。
想象一下 DLLImport 是一个运行时选项,您放入的路径在运行时用于查找 dll,因此您必须自己部署 c++ dll 并使其可用于 .net 应用程序,通常在同一bin 文件夹。
【讨论】:
我希望它可以设置为在本地复制非托管 DLL,因为这就是 Visual Studio 使用 .NET 类库 DLL 作为标准所做的事情,实际上这并没有太大的不同。最终它们也会在运行时加载。 .NET 编译模型要么是“在 GAC 中”,要么是“在一个文件夹中”。如果非托管代码支持使这更容易,那就太好了。尽管正如您所说,这一切都在运行时并且 DLL 可以来自任何地方,但对于编写模块化库的开发人员来说,一些额外的工具来帮助管理位置在我看来不会出错。以上是关于如何添加对由 C# 项目调用的非托管 C++ 项目的引用?的主要内容,如果未能解决你的问题,请参考以下文章