VC++在解决方案中从非/clr项目的函数调用/clr工程的函数

本文关键字:clr 函数调用 函数 项目 VC++ 解决方案 | 更新日期: 2023-09-27 18:27:58

在问这个问题之前,我提到了这个有点类似的问题,但无法解决我的问题

我正在查看一个有许多解决方案的旧应用程序。问题发生在其中一个解决方案中(比如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:库

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

错误LNK2005:"private:__thiscall type_info::type_info(类type_info常量&)"(?0type_info@@AAE@ABV0@@Z) 已在中定义libcmtd.lib(typeinfo.obj)

错误LNK2005:"private:class type_info&__thiscalltype_info::operator=(类type_info const&)"(?4类型信息@@AAEAAV0@ABV0@@Z) 已在中定义libcmtd.lib(typeinfo.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 __cdeclClass::foo()"引用于函数{P1的某个函数}

VC++在解决方案中从非/clr项目的函数调用/clr工程的函数

我终于能够通过大量的试错和StackOverflow内外的互联网搜索来解决这个问题。至少链接器错误已经消失,不知道还会弹出什么其他东西,但这是一个好迹象。
我将尽可能多地记录以下内容:

换言之,问题:

如何链接同一项目下的两个dll,其中一个是/clr,另一个不是clr

实际问题简述:

  • 一切都很好,直到一个需求出现在解决方案,我必须调用C#模块
  • 为了调用C#代码(或托管代码),项目必须是/clr项目
  • 如果项目包含所有.cpp代码而不包含.c代码,则该项目可以是/clr
  • 在我的案例中,解决方案中的主要项目包含.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,添加适当的includeCCD_ 12下的路径;这样你就可以包括P2的新头文件在P1的源文件中的任何位置
  • 再次针对项目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有意义,在同一页中,您应该设置CCD_ 17和CCD_
  • 对于项目P2,再次转到Properties -> Configuration Properties -> Linker -> General -> Ignore Import Library -> No;我这么做是因为P1也是这样
  • 现在最重要的部分是:无论你在里面添加了什么类P2的新头文件,我们需要提到__declspec(dllexport)(或__declspec(dllimport),不确定,但两部作品);我从这个问题和这个问题中得到了这些关键信息

通过以上步骤,构建成功了
可能会遗漏一些东西,因此我面临一些运行时问题。然而,至少我能够在相同的解决方案下链接两个DLL项目,无论是否使用/clr

好吧,你没有链接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。Project+属性,C/C++,代码生成,运行库设置。目前还不清楚哪个项目存在这个问题。要小心从其他地方得到的.lib文件,这些文件是用错误的设置编译的。如果您不知道哪一个是麻烦制造者,那么为"-MTd"grep文件,.lib文件包含原始编译命令的副本。

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

链接它们的方法是有一个没有.NET方面的公共头文件,只有一个类或函数原型,并有用/clr编译的文件中的实现

这里的主要"陷阱"是:

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

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

如果你想要一个工作项目,给我发一个PM,我会给你发一个2012年的项目,它就是这样做的。

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

把它放进2012年的解决方案中,然后编译它。它应该"只适用于"某些事情。

至于如何制作:

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

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