VC++在解决方案中从非/clr项目的函数调用/clr工程的函数
VC++ Calling a function of /clr project from a function of non /clr project within a solution
在问这个问题之前,我提到了这个有点类似的问题,但无法解决我的问题
我正在查看一个有许多解决方案的旧应用程序。问题发生在其中一个解决方案中(比如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的某个函数}
我终于能够通过大量的试错和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年的解决方案中,然后编译它。它应该"只适用于"某些事情。
至于如何制作:
- 制作一个新的Win32控制台应用程序
- 将.cpp文件和.h文件都添加到项目中
- 使用PURE C++代码在.h文件中定义函数和/或类,而不是任何需要CLR的东西(没有
String^
或其他什么),但可以向前声明指向将包含这些东西的类的指针。您将在示例项目中看到这一点 - 右键单击将包含类的实现的.cpp文件,并确保在"C/C++->General"选项下启用"Common Language Runtime support"。您还必须将"调试信息格式"更改为"运行时数据库"。在"代码生成"下,您还必须更改"启用最小重建"为"否","启用C++异常"为"是,带SEH异常"或"否"以及"基本运行时检查"为"默认"。将"预编译头"更改为"不使用"。有一种方法可以使用它们,但这需要大量的额外工作
- 特殊C++文件中函数和方法的主体可以包含CLR代码,并且应该包含
- 如果你想引用其他程序集,你必须保存,然后关闭并重新打开解决方案,然后你可以在项目菜单的"公共属性->框架和引用"下添加它们。如果要使框架版本为4.5(默认为4.0),则必须更改项目文件,并确保
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
行位于.vcxproj
文件的正确位置。在所附的项目中,它是正确的4.5
所以我试过了,它对我有效。如果不有效,请检查附件,看看它是否适用于VS 2012。
- 函数调用中参数的顺序重要吗
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 变量没有改变?通过向量的函数调用
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 模板函数调用
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 析构函数调用
- 成员函数调用和C++对象模型
- 使用共享指针的函数调用,其对象应为 const
- C++:编译时检查匹配的函数调用对?
- 函数调用C++中的参数太少
- 来自 DLL 的函数调用 [表观调用的括号前面的表达式必须具有(指向-)函数类型]
- 返回指向对象的指针的函数调用是否为 prvalue?
- C++ 如何重载 [] 运算符并进行函数调用
- 代码的效率. 转到和函数调用
- 是同一作用域的函数部分中的函数调用
- 如何封装一个函数,以便它只能由同一类中的一个其他函数调用?
- VC++在解决方案中从非/clr项目的函数调用/clr工程的函数