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

时间:2013-08-17 14:21:05

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

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

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

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

并发出警告:

  

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

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

  

错误LNK2005:“private:__ thiscall type_info :: type_info(class   type_info const&)“(?? 0type_info @@ AAE @ ABV0 @@ Z)已在   libcmtd.lib(typinfo.obj)

     

错误LNK2005:“private:class 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抛出了上述错误。

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

  

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

3 个答案:

答案 0 :(得分:5)

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

换句话说问题:

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

实际问题简述:

  • 一切都运转良好,直到一个要求出现在其中一个 解决方案,我必须调用C#模块。
  • 为了调用C#代码(或托管代码),项目必须是a /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下的路径;这样你就可以包括在内 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,请在DLL
  • 下将配置设置为Properties -> Configuration Properties -> General -> Project Defaults -> Configuration Type -> Dynamic Library (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
  • 获得了这一重要信息

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

答案 1 :(得分:3)

嗯,你没有链接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文件包含原始编译命令的副本。

答案 2 :(得分:3)

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

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

主要&#34;陷阱&#34;这是:

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

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

如果您想要一个有效的项目,请给我发一个PM,然后我会通过电子邮件向您发送一个完成此项目的2012项目。

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

将其纳入2012年的解决方案,然后进行编译。它应该&#34;只是工作&#34;对于事物。

至于如何制作:

  1. 制作一个新的Win32控制台应用程序。
  2. 将.cpp文件和.h文件添加到项目中。
  3. 使用PURE C ++代码定义.h文件中的函数和/或类,而不是任何需要CLR(无String^或其他任何东西)的函数和/或类,但是你可以转发声明指向将包含这些东西的类的指针。您将在示例项目中看到这一点。
  4. 右键单击将包含该类的实现的.cpp文件,并确保&#34;公共语言运行时支持&#34;在&#34; C / C ++ - &gt; General&#34;下启用选项。您还必须更改&#34;调试信息格式&#34;到&#34;运行时数据库&#34;在&#34;代码生成&#34;你还必须改变&#34;启用最小重建&#34; to&#34; No&#34;和&#34;启用C ++异常&#34;对于SEH Exceptions&#34;是和#34;或&#34;否&#34;和&#34;基本运行时检查&#34;到&#34;默认&#34;。更改&#34;预编译标题&#34;到&#34;不使用&#34;。有一种方法可以使用它们,但它需要做很多额外的工作。
  5. 特殊C ++文件中的函数和方法的主体可以包含CLR代码,并且应该。
  6. 如果要引用其他程序集,则必须先保存,然后关闭并重新打开解决方案,然后将其添加到&#34; Common Properties-&gt; Frameworks and references&#34;在项目菜单中。如果要使框架版本为4.5(默认为4.0),则必须更改项目文件并确保<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>文件中的第.vcxproj行位于{{1}}文件中。它在附加项目中正确4.5。
  7. 所以我试过了,这对我有用。如果没有,请检查附件,看看这对VS 2012是否有效。