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

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

我正在寻找一个包含许多解决方案的旧应用程序。 问题出现在其中一个解决方案中(比如说S)。 情况如下:

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

和警告:

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

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

错误LNK2005:已在libcmtd.lib(typinfo.obj)中定义的“private:__thiscall type_info :: type_info(class type_info const&)”(?? 0type_info @@ AAE @ ABV0 @@ Z)

错误LNK2005:已在libcmtd.lib(typinfo.obj)中定义的“private:class type_info&__thiscall type_info :: operator =(class type_info const&)”(?? 4type_info @@ AAEAAV0 @ ABV0 @@ Z)

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

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

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

更新

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

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

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

如果我从静态库(.lib)创建P2到动态链接库(.dll),那么上述错误就会消失。 对于未定义的引用, foo本身会出现新的链接器错误:

错误LNK2019:未解析的外部符号“public:void __cdecl Class :: foo()”在函数中引用{P1的某个函数}

我终于能够通过大量的试验和错误以及进出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 ->
  • 对于新项目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) ,不确定但两者都有效); 我从这个问题和这个问题得到了这个重要的信息

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

好吧,你没有链接C#代码,这是不可能的,所以这不是问题的根源。 警告是核心问题的第一个提示,您正在尝试链接使用/ MT编译的代码,因此依赖于libcmtd.lib(CRT的静态版本)。 您的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文件,这些文件只是用错误的设置编译的。 如果你不知道麻烦制造者是哪个,那么grep文件“-MTd”,.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 ++ – >常规”选项下启用“公共语言运行时支持”。 您还必须将“调试信息格式”更改为“运行时数据库”在“代码生成”下,您还必须将“启用最小重建”更改为“否”,并将“启用C ++例外”更改为“是和SEH例外”或“否”和“基本运行时检查”到“默认”。 将“预编译标题”更改为“不使用”。 有一种方法可以使用它们,但这是一项额外的工作。
  5. 特殊C ++文件中的函数和方法的主体可以包含CLR代码,并且应该包含。
  6. 如果要引用其他程序集,则必须保存,然后关闭并重新打开解决方案,然后可以在项目菜单中的“公共属性 – >框架和引用”下添加它们。 如果要使框架版本为4.5(默认为4.0),则必须更改项目文件并确保.vcxproj文件中的正确位置中存在v4.5 .vcxproj 。 它在附加项目中正确4.5。

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