【问题标题】:VC++ Calling a function of /clr project from a function of non /clr project within a solutionVC++ 从解决方案中的非 /clr 项目的函数调用 /clr 项目的函数
【发布时间】:2013-08-19 20:00:13
【问题描述】:

我在问这个之前提到了this somewhat similar question,但无法解决我的问题

我正在查看具有许多解决方案的旧应用程序。问题发生在其中一种解决方案中(比如 S)。情况如下:

  • S 中的项目(比如 P1)包含所有 C/C++ 文件,需要调用 C# 函数
  • 由于 P1 还包含 .c 文件,我不能将 /clr 选项与 那
  • 如果我将 P1 中的 .c 文件编译为 .cpp 文件,则会生成很多 错误,我不打算更改旧 .c 文件中的源代码
  • 所以我创建了另一个启用/clr 的项目(比如P2)并创建了一个头文件 函数声明和函数定义的 .cpp 文件;这 C#调用是在它下面进行的; P2 编译正常
  • 请注意,P1 是一个 .dll,而 P2 是作为静态库创建的;
  • P2 在 P1 的“框架和参考”中提及

还有一个警告:

警告 LNK4098:defaultlib 'MSVCRT' 与使用其他库冲突; 使用 /NODEFAULTLIB:library

现在有了所有这些,我在 P1 中遇到 3 个链接器错误:

错误 LNK2005:“私人:__thiscall type_info::type_info(class type_info const &)" (??0type_info@@AAE@ABV0@@Z) 已经定义在 libcmtd.lib(typinfo.obj)

错误 LNK2005:“私有:类 type_info & __thiscall type_info::operator=(class type_info const &)" (??4type_info@@AAEAAV0@ABV0@@Z) 已经定义在 libcmtd.lib(typinfo.obj)

错误 LNK1169:找到一个或多个多重定义的符号

包括本网站在内的许多在线论坛都会出现此错误。但不知何故,在尝试了这些选项后我无法修复它(我是 .NET 框架的新手)。
重要的一点是,即使我从 P2 中删除了 C# 代码,也会出现同样的错误。

修复它的正确方法是什么?

更新

P2 只包含 1 个带有函数声明的头文件和 1 个带有函数定义的源文件,这是对 C# 方法的 1 行调用;例如

void Class::foo () {  // A static function inside Class
  std::string x = marshal_as<std::string>(C#_function);
  // ...
}

新添加了 P2 以使用 /clr 进行编译(删除 P2 使解决方案编译正常)。
我正在使用 /MD[d] 选项编译 P1 和 P2。上面的错误是P1抛出的。

如果我将 P2 从静态库 (.lib) 制作为动态链接库 (.dll),那么上述错误就会消失。新的链接器错误来自foo 本身,用于未定义的引用:

错误 LNK2019:无法解析的外部符号“public: void __cdecl Class::foo()" 在函数 { P1 的某个函数}

中引用

【问题讨论】:

  • 从本机代码中创建一个静态库要容易得多,然后将其拉入创建实际 DLL 的托管(C++/CLI,使用/clr)项目中。这使您可以将本机与托管的项目设置分开,同时让链接步骤了解托管。
  • @BenVoigt,您能否将其放入答案中并提供更多详细信息(哪个菜单,哪个选项等)。如果效果好,我会很乐意接受。我是 VC++ 的新手,这让我很烦恼!请注意,在当前的解决方案 S 中,由于存在 .c 文件,我无法将 P1 设为 /clr
  • 嗯,哪个菜单等将取决于哪个版本的 Visual Studio。
  • 如果您将项目 2 设为 DLL,将项目 1 设为静态库,并切换项目依赖项,事情可能就开始工作了。
  • @BenVoigt,这就是重点!!请把它放在一个正确的答案中(只要你有时间),我会接受的。顺便说一句,它是 VC++2010。我可以理解 P2 必须是 .dll (我已经设置了)。您可能想解释为什么将 P1 从 .dll 更改为 .lib 有效。

标签: c# c++ visual-c++ clr linker-errors


【解决方案1】:

嗯,你没有链接 C# 代码,这是不可能的,所以这不是问题的根源。该警告是核心问题的第一个提示,您正在尝试链接使用 /MT 编译的代码,因此依赖于 CRT 的静态版本 libcmtd.lib。您的 C++/CLI 代码将始终使用 /MD 编译,因此依赖于 msvcrtd.lib,即存储在 DLL 中并可以在多个模块之间共享的 CRT 版本。

您不能在一个可执行文件中混合使用两种版本的 CRT,这就是为什么链接器对象带有 LNK4098。当它看到 type_info 类实现的两个副本时,链接失败,一个来自 libcmtd.lib,另一个来自 msvcrtd.lib,并且无法确定您真正想要哪个。

此外,C++/CLI 项目有一个非常严格的要求,即您必须使用 /MD 并与 msvcrtd.lib 链接,不支持 CRT 的静态版本。您必须返回到使用 /MT 编译代码的项目并将设置更改为 /MD。项目 + 属性、C/C++、代码生成、运行时库设置。目前尚不清楚哪个特定项目存在此问题。当心您从其他地方获得的 .lib 文件,这些文件刚刚使用错误的设置进行编译。如果您不知道哪个是麻烦制造者,请使用“-MTd”grep 文件,.lib 文件包含原始编译命令的副本。

【讨论】:

  • 感谢您的意见。我之前尝试过/MD 选项,但没有帮助。或者可能是配置搞砸了。但是,如果我将 P2 从 .lib 更改为 .dll,其他错误将从 P1 消失,并且仅在 P1 中出现上述错误。还有什么可以补充的吗?
  • 我同意他没有在此处链接 C# 代码,但可以使用 netmodules。
  • @BenVoigt,HansPassant,我找到了针对我的问题的解决方案,并已记录为答案。您的意见也很有用。谢谢。
【解决方案2】:

我终于能够通过大量试验和错误以及进出 StackOverflow 的互联网搜索来解决这个问题。至少链接器错误消失了,不知道还会弹出什么其他东西,但这是一个好兆头。
我会尽量在下面记录:

换句话说问题:

如何链接同一项目下的2个dll,其中一个为/clr,另一个为非clr

实际问题简介:

  • 一切正常,直到有需求出现 解决方案,我必须调用 C# 模块。
  • 为了调用 C# 代码(或托管代码),项目必须是 /clr项目
  • 一个项目可以是/clr,如果它包含所有.cpp代码并且没有.c代码
  • 就我而言,解决方案中的主项目包含 .c 文件; 如果我尝试使用 .cpp 选项编译它,那么它将提供很多 错误,由于遗留原因,我无法更改该文件
  • 所以最好的选择是使用 .h 和 .cpp 文件创建一个新项目 它将包含接口方法及其实现 (分别调用 C# 或 C++/CLI)

到目前为止还不错,但是当新项目(P2)的功能定义没有与原始项目(P1)链接时,问题就来了。它给出了各种链接器错误。

解决办法:

这些步骤适用于 VC++2010 的新手用户(比如我)。

配置 P1

  • 右键单击解决方案 S 和 Add -> New Project -> Other languages -> VC++ -> CLR empty project 并将其命名(例如 P2);在项目的适当部分添加头文件和 .cpp 文件
  • 这会自动设置Properties -> Configuration Properties -> C/C++ -> Code Generation -> Runtime Library to Multi threaded DLL: /MD[d];哪个是必不可少的
  • 对于原始项目 P1,添加适当的包含 Properties -> Configuration Properties -> C/C++ -> General -> Additional Include Directories 下的路径;这样你就可以包括 P1 源文件中任意位置的 P2 新头文件
  • 同样对于项目 P1,转到 Properties -> Common Properties -> Framework and Reference -> Add New Reference,您应该可以 在那里看到P2;只需添加它

配置 P2

  • 第一步是可选的,因为构建成功,即使没有 那个,但我正在记录; Properties -> Common Properties -> Framework and References -> Add New Reference -> <Select the C# or whatever external DLL you would want to call from P2>
  • 对于新项目P2,在Properties -> Configuration Properties -> General -> Project Defaults -> Configuration Type -> Dynamic Library (DLL)下设置配置为DLL
  • 如果对项目 P2 有意义,则应在同一页面中设置 Output DirectoryIntermediate Directory 也与 P1 同步(不完全相同)
  • 再次为项目 P2 转到 Properties -> Configuration Properties -> Linker -> General -> Ignore Import Library -> No;我这样做是因为 P1也是这样
  • 现在最重要的部分:无论你在里面添加了什么类 P2的新头文件,我们需要提到 __declspec(dllexport)(或__declspec(dllimport),不确定但 两者都有效);我从this questionthis question 获得了这个重要信息

通过以上步骤构建成功!
可能会遗漏一些东西,因此我面临一些运行时问题。但是,至少我能够在有和没有/clr 的同一解决方案下链接 2 个 DLL 项目。

【讨论】:

    【解决方案3】:

    另一个解决方案是使您的项目成为混合模式的可执行文件。您可以将不同的 C++ 文件编译为不同的东西。大部分都将保持原样,但您可以为各个 C++ 文件设置不同的编译器设置,并使用/clr 编译器标志编译只是那些。您可以直接从该 C++/CLI 代码调用 .NET 对象。

    如何链接它们是有一个共同的头文件,它没有 .NET 方面,只是一个类或函数原型,并在文件中具有这些的实现/clr编译。

    这里的主要“陷阱”是:

    • 如果您只编译一个文件/clr,如预编译头文件等该文件,则需要禁用许多其他选项。您仍然可以在项目的其余部分使用它,但不是那个。基本上,添加新文件,使用 /clr 选项,然后开始检查编译器错误,取消检查并更改属性中的字段,直到它正常工作。
    • 三重确保您更改的是一个文件的编译选项,而不是整个项目的。右键单击 .cpp 文件本身。
    • .h 文件包含在您需要从(非托管)调用函数的位置以及您要实现它们的位置,即使用/clr 编译的.cpp 文件。
    • 另一个网站的“作弊”是向项目中添加一个“UI->Windows 窗体”,因为那是 CLR,它会添加一个文件,然后为您设置项目的其余部分。然后您只需将其删除,然后添加回您想要的实际源文件。作品类型。
    • 另一个秘籍是添加一个类型为“C++->CLR->CLR Console Application”的新项目,然后比较项目文件,这样你就可以得到一些东西,比如FrameWork版本等等
    • 完成所有这些操作后,保存、关闭并重新打开您的解决方案。然后在 C++ 项目的“属性”中,您将能够添加基本内容,例如对 .NET 程序集的引用,例如 System 以及 CLR 部分所需的任何其他内容。与上述不同的是,编译器选项需要针对每个文件,在这种情况下,程序集包含的是项目范围的。

    所以要非常小心,只为您想要编译的可以访问 .NET 的特定 .cpp 文件(可能只有 1 个)设置 /clr。编译的其余部分应该不受影响。然后只需从这些特定的 C++ 文件中调用您的托管静态方法(并实例化类,如有必要)。

    如果您想要一个工作项目,请给我发邮件,我会通过电子邮件向您发送一个 2012 年的项目,该项目正是这样做的。

    编辑:这是项目:http://www.mediafire.com/download/rfhk5hx6x27fp0m/MixedModeExecutable.7z

    将其放入 2012 年的解决方案中,然后编译它。它应该“正常工作”。

    至于制作方法:

    1. 制作一个新的 Win32 控制台应用程序。
    2. 将 .cpp 文件和 .h 文件添加到项目中。
    3. 使用纯 C++ 代码在 .h 文件中定义您的函数和/或类,而不是任何需要 CLR(没有 String^ 或其他),但您可以前向声明指向将包含此类内容的类的指针。您将在示例项目中看到这一点。
    4. 右键单击将包含类的实现的 .cpp 文件,并确保在“C/C++->常规”选项下启用“公共语言运行时支持”。您还必须将“调试信息格式”更改为“运行时数据库”在“代码生成”下,您还必须将“启用最小重建”更改为“否”,并将“启用 C++ 异常”更改为“有 SEH 异常”或“否”和“基本运行时检查”为“默认”。将“预编译头文件”更改为“不使用”。有一种使用它们的方法,但需要做很多额外的工作。
    5. 特殊 C++ 文件中的函数和方法的主体可以包含 CLR 代码,并且应该。
    6. 如果要引用其他程序集,则必须保存,然后关闭并重新打开解决方案,然后可以在项目菜单的“公共属性->框架和引用”下添加它们。如果要使框架版本为 4.5(默认为 4.0),则必须更改项目文件并确保 &lt;TargetFrameworkVersion&gt;v4.5&lt;/TargetFrameworkVersion&gt; 行位于 .vcxproj 文件中的正确位置。在附加项目中正确的是 4.5。

    所以我尝试了,它对我有用。如果不是,请检查附件并查看这是否适用于 VS 2012。

    【讨论】:

    • 这听起来很有趣。我尝试使用许多排列组合制作单个文件/clr,但没有运气。如果你能提供一步一步的信息,如何执行,那么这对我和其他用户会有很大的帮助。如果它有效,那么我会接受你的回答。
    • 编辑了一个示例项目和更明确的说明。
    • 非常好的解决方案!密切注意“有一个与 NO .net 方面相同的头文件”。头文件充当 .NET clr 项目的包装器。这使您可以轻松地在 .cpp 文件中使用 .Net 代码!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 2017-12-23
    • 2013-05-26
    • 2012-08-15
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多